Merge branch 'master' into vray

This commit is contained in:
annatisch 2017-07-05 20:12:41 -07:00
Родитель b57dd3cae8 e9ccc2eb27
Коммит 0d5ab7698a
46 изменённых файлов: 1243 добавлений и 1156 удалений

Просмотреть файл

@ -10,10 +10,8 @@
<OutputPath>.</OutputPath>
<Name>Maya.Client</Name>
<RootNamespace>Maya.Client</RootNamespace>
<InterpreterId>
</InterpreterId>
<InterpreterVersion>
</InterpreterVersion>
<InterpreterId>{a76b797f-9426-4cd7-a078-9af9c9e4b437}</InterpreterId>
<InterpreterVersion>2.7</InterpreterVersion>
<SearchPath>azure_batch_maya\scripts\;azure_batch_maya\scripts\ui\;tests\data\;tests\data\modules\</SearchPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
@ -89,7 +87,7 @@
<Compile Include="azure_batch_maya\scripts\exception.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="azure_batch_maya\scripts\history.py">
<Compile Include="azure_batch_maya\scripts\jobhistory.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="azure_batch_maya\scripts\submission.py" />
@ -115,7 +113,7 @@
<Compile Include="azure_batch_maya\scripts\ui\ui_environment.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="azure_batch_maya\scripts\ui\ui_history.py">
<Compile Include="azure_batch_maya\scripts\ui\ui_jobhistory.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="azure_batch_maya\scripts\ui\ui_pools.py">
@ -149,9 +147,6 @@
<Compile Include="tests\test_submission.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="tests\__init__.py">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Folder Include="azure_batch_maya\icons\" />
@ -182,7 +177,7 @@
<Content Include="azure_batch_maya\icons\loading_preview.png" />
<Content Include="azure_batch_maya\icons\no_preview.png" />
<Content Include="azure_batch_maya\mel\create_shelf.mel" />
<Content Include="azure_batch_maya\plug-in\SLA.html" />
<Content Include="azure_batch_maya\plug-in\EULA.html" />
<Content Include="azure_batch_maya\scripts\tools\ignored_plugins.json">
<SubType>Code</SubType>
</Content>
@ -194,6 +189,8 @@
</Content>
<Content Include="azure_batch_maya\templates\arnold-basic-windows.json" />
<Content Include="azure_batch_maya\templates\arnold-basic-linux.json" />
<Content Include="azure_batch_maya\templates\mayaSoftware-basic-linux.json" />
<Content Include="azure_batch_maya\templates\mayaSoftware-basic-windows.json" />
<Content Include="CHANGES.txt" />
<Content Include="CONTRIBUTING.md">
<SubType>Code</SubType>
@ -206,6 +203,19 @@
<SubType>Code</SubType>
</Content>
</ItemGroup>
<ItemGroup>
<Interpreter Include="env\">
<Id>{a76b797f-9426-4cd7-a078-9af9c9e4b437}</Id>
<BaseInterpreter>{9a7a9026-48c1-4688-9d5d-e5699d47d074}</BaseInterpreter>
<Version>2.7</Version>
<Description>env (Python 64-bit 2.7)</Description>
<InterpreterPath>Scripts\python.exe</InterpreterPath>
<WindowsInterpreterPath>Scripts\pythonw.exe</WindowsInterpreterPath>
<LibraryPath>Lib\</LibraryPath>
<PathEnvironmentVariable>PYTHONPATH</PathEnvironmentVariable>
<Architecture>Amd64</Architecture>
</Interpreter>
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<PtvsTargetsFile>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets</PtvsTargetsFile>

Просмотреть файл

@ -1 +1,30 @@
v0.9.0 - Core re-written to use latest Azure Batch Python SDK and Batch Extensions template files.
2017-07-05 v0.10.0
------------------
- Exposed low priority VM allocation for pools
- Exposed thread configuration for uploads/downloads/task submission
- Added coloured label to confirm successful authentication
- Fixed some bugs in job status loading
2017-06-23 v0.9.3
-----------------
- Added threading to output downloads
- Added threading to task submission
- Made task counting for job detail display load asynchronously
2017-06-12 v0.9.2
-----------------
- Fixed bug in OS detection of Job Watcher
- Updated VM Image references
2017-05-24 v0.9.1
-----------------
- Expanded Pool display UI to show pools created via the Azure portal
- Fixed bug where Pool couldn't be displayed if created with a non-current image
2017-05-23 v0.9.0
-----------------
- Core re-written to use latest Azure Batch Python SDK and Batch Extensions template files.

Просмотреть файл

@ -1,5 +1,5 @@
The Azure Batch Maya Sample ver. 0.9.0
The Azure Batch Maya Sample ver. 0.10.0
Copyright (c) Microsoft Corporation
All rights reserved.

Просмотреть файл

@ -35,7 +35,7 @@ ensure the updated dependencies are loaded correctly.
Before using the plug-in, it will need to be authenticated using your Azure Batch and Azure Storage account keys.
In order to retrieve this information:
1. Open the Azure management portal (ms.portal.azure.com).
1. Open the Azure management portal (portal.azure.com).
2. Select Azure Batch Accounts in the left-hand menu. This can be found under `More Services` in the `Compute` category.
3. Select your account in the list. Copy and paste the account URL into `Service` field of the plug-in UI. Paste the account name into the `Batch Account` field.
4. In the portal, select `Keys` on the left-hand menu. Copy and paste one of the access keys into the `Batch Key` field in the plug-in.

Просмотреть файл

@ -9,7 +9,7 @@ global proc run_guiStarter()
global proc openMissionControl()
{
python("import webbrowser\nwebbrowser.open(\"https://ms.portal.azure.com\", 2, True)");
python("import webbrowser\nwebbrowser.open(\"https://portal.azure.com\", 2, True)");
}

Просмотреть файл

@ -1,30 +1,7 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import os
import sys
@ -41,26 +18,39 @@ from default import AzureBatchRenderJob, AzureBatchRenderAssets
class ArnoldRenderJob(AzureBatchRenderJob):
render_engine = 'arnold'
def __init__(self):
self._renderer = "arnold"
self.label = "Arnold"
self._renderer = 'arnold'
self.label = 'Arnold'
self.log_levels = [
"0 - Errors",
"1 - Warnings + Info",
"2 - Debug"
]
def settings(self):
if self.scene_name == "":
if self.scene_name == '':
job_name = "Untitled"
else:
job_name = str(os.path.splitext(os.path.basename(self.scene_name))[0])
file_prefix = mel.eval("getAttr defaultRenderGlobals.imageFilePrefix")
file_prefix = cmds.getAttr("defaultRenderGlobals.imageFilePrefix")
if file_prefix:
file_prefix = os.path.split(file_prefix)[1]
else:
file_prefix = "<Scene>"
self.job_name = self.display_string("Job Name: ", job_name)
self.output_name = self.display_string("Output Prefix: ", file_prefix)
self.job_name = self.display_string("Job name: ", job_name)
self.output_name = self.display_string("Output prefix: ", file_prefix)
self.start = self.display_int("Start frame: ", self.start_frame, edit=True)
self.end = self.display_int("End frame: ", self.end_frame, edit=True)
self.step = self.display_int("Frame step: ", self.frame_step, edit=True)
try:
log_level = cmds.getAttr("defaultArnoldRenderOptions.log_verbosity")
except ValueError:
log_level = 1
self.logging = self.display_menu("Logging: ", self.log_levels, log_level+1)
def get_title(self):
return str(cmds.textField(self.job_name, query=True, text=True))
@ -68,45 +58,48 @@ class ArnoldRenderJob(AzureBatchRenderJob):
return True
def get_jobdata(self):
if self.scene_name == "":
if self.scene_name == '':
raise ValueError("Current Maya scene has not been saved to disk.")
pending_changes = cmds.file(query=True, modified=True)
if not pending_changes:
return self.scene_name, [self.scene_name]
options = ["Save and Continue",
"Don't Save and Continue",
"Cancel"]
answer = cmds.confirmDialog(title='Unsaved Changes',
message='There are unsaved changes. Proceed?',
button=options,
defaultButton=options[0],
cancelButton=options[2],
dismissString=options[2])
if answer == options[2]:
raise Exception("Submission Aborted")
if answer == options[0]:
options = {
'save': "Save and continue",
'nosave': "Continue without saving",
'cancel': "Cancel"
}
answer = cmds.confirmDialog(title="Unsaved Changes",
message="There are unsaved changes. Continue?",
button=options.values(),
defaultButton=options['save'],
cancelButton=options['cancel'],
dismissString=options['cancel'])
if answer == options['cancel']:
raise Exception("Submission cancelled")
if answer == options['save']:
cmds.SaveScene()
return self.scene_name, [self.scene_name]
def get_params(self):
params = {}
params["frameStart"] = cmds.intField(self.start, query=True, value=True)
params["frameEnd"] = cmds.intField(self.end, query=True, value=True)
params["frameStep"] = cmds.intField(self.step, query=True, value=True)
params["renderer"] = "arnold"
params['frameStart'] = cmds.intField(self.start, query=True, value=True)
params['frameEnd'] = cmds.intField(self.end, query=True, value=True)
params['frameStep'] = cmds.intField(self.step, query=True, value=True)
params['renderer'] = self._renderer
params['logLevel'] = int(cmds.optionMenu(self.logging, query=True, select=True)) - 1
return params
class ArnoldRenderAssets(AzureBatchRenderAssets):
assets = []
render_engine = "arnold"
render_engine = 'arnold'
file_nodes = {
"aiStandIn": ["dso"],
"aiPhotometricLight": ["aiFilename"],
"aiVolume": ["dso", "filename"],
"aiImage": ["filename"]
'aiStandIn': ['dso'],
'aiPhotometricLight': ['aiFilename'],
'aiVolume': ['dso', 'filename'],
'aiImage': ['filename']
}
def check_path(self, path):
@ -126,7 +119,7 @@ class ArnoldRenderAssets(AzureBatchRenderAssets):
nodes = cmds.ls(type=node_type)
for node in nodes:
for attr in attributes:
collected.append(cmds.getAttr(node + "." + attr))
collected.append(cmds.getAttr(node + '.' + attr))
for path in collected:
self.assets.append(self.check_path(path))
return self.assets

Просмотреть файл

@ -1,30 +1,7 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import os
@ -94,12 +71,13 @@ class AzureBatchRenderJob(object):
else:
return cmds.text(label=value, align='left')
def display_menu(self, label, options):
def display_menu(self, label, options, selected):
cmds.text(label=label, align='right')
menu = cmds.optionMenu()
for opt in options:
cmds.menuItem(label=opt)
cmds.setParent('..')
cmds.optionMenu(menu, edit=True, select=selected)
return menu
def display_button(self, label, cmd):
@ -153,7 +131,6 @@ class AzureBatchRenderJob(object):
horizontalScrollBarThickness=0,
verticalScrollBarThickness=3,
parent=layout,
#width=360,
height=260)
self.subLayout = cmds.rowColumnLayout(

Просмотреть файл

@ -1,30 +1,7 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import os
import sys
@ -38,8 +15,10 @@ from default import AzureBatchRenderJob, AzureBatchRenderAssets
class AzureBatchMayaJob(AzureBatchRenderJob):
render_engine = "mayaSoftware"
def __init__(self):
self._renderer = "mayaSoftware"
self._renderer = "sw"
self.label = "Maya Software"
def settings(self):
@ -66,36 +45,35 @@ class AzureBatchMayaJob(AzureBatchRenderJob):
return True
def get_jobdata(self):
if self.scene_name == "":
if self.scene_name == '':
raise ValueError("Current Maya scene has not been saved to disk.")
pending_changes = cmds.file(query=True, modified=True)
if not pending_changes:
return [self.scene_name]
options = ["Save and Continue",
"Don't Save and Continue",
"Cancel"]
answer = cmds.confirmDialog(title='Unsaved Changes',
message='There are unsaved changes. Proceed?',
button=options,
defaultButton=options[0],
cancelButton=options[2],
dismissString=options[2])
if answer == options[2]:
raise Exception("Submission Aborted")
if answer == options[0]:
return self.scene_name, [self.scene_name]
options = {
'save': "Save and continue",
'nosave': "Continue without saving",
'cancel': "Cancel"
}
answer = cmds.confirmDialog(title="Unsaved Changes",
message="There are unsaved changes. Continue?",
button=options.values(),
defaultButton=options['save'],
cancelButton=options['cancel'],
dismissString=options['cancel'])
if answer == options['cancel']:
raise Exception("Submission cancelled")
if answer == options['save']:
cmds.SaveScene()
return [self.scene_name]
return self.scene_name, [self.scene_name]
def get_params(self):
params = {}
params["StartFrame"] = cmds.intField(self.start, query=True, value=True)
params["EndFrame"] = cmds.intField(self.end, query=True, value=True)
params["Renderer"] = "sw"
params["JobFile"] = os.path.basename(self.scene_name)
filename = str(cmds.textField(self.output_name, query=True, text=True))
if '/' in filename or '\\' in filename:
raise ValueError("Subfolders not supported in output filename.")
params["OutputName"] = filename
params["frameStart"] = cmds.intField(self.start, query=True, value=True)
params["frameEnd"] = cmds.intField(self.end, query=True, value=True)
params["frameStep"] = cmds.intField(self.step, query=True, value=True)
params["renderer"] = self._renderer
return params

Просмотреть файл

@ -1,30 +1,7 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import urllib
import os
@ -53,44 +30,43 @@ warnings.simplefilter('ignore')
INSTALL_DIR = os.path.normpath(
os.path.join(cmds.internalVar(userScriptDir=True), 'azure-batch-libs'))
sys.path.append(INSTALL_DIR)
REQUIREMENTS = [
"pathlib==1.0.1",
]
NAMESPACE_PACAKGES = [
#"azure-mgmt-nspkg==2.0.0",
"azure-mgmt-batch==4.0.0",
"azure-mgmt-storage==1.0.0rc1",
"azure-mgmt-storage==1.0.0",
"azure-common==1.1.5",
"azure-batch==3.0.0",
"azure-storage==0.32.0",
]
VERSION = "0.9.0"
SLA_PREF = "AzureBatch_SLA"
VERSION = "0.10.0"
EULA_PREF = "AzureBatch_EULA"
SHELF_FILE = "shelf_AzureBatch.mel"
cmd_name = "AzureBatch"
fMayaExitingCB = None
os.environ["AZUREBATCH_VERSION"] = VERSION
def sla_prompt():
"""Open prompt for T's & C's agreement."""
def eula_prompt():
"""Open prompt for terms and conditions."""
current_file = inspect.getfile(inspect.currentframe())
current_dir = os.path.dirname(os.path.abspath(current_file))
SLA = os.path.join(current_dir, "SLA.html")
eula = os.path.join(current_dir, "EULA.html")
form = cmds.setParent(q=True)
cmds.formLayout(form, e=True, width=500)
heading = cmds.text(
l='Maya Cloud Rendering Service Level Agreement', font="boldLabelFont")
l='Maya Cloud Rendering License Agreement', font="boldLabelFont")
text = cmds.text(l="By loading this plug-in you are agreeing to "
"the following terms and conditions.")
if not os.path.exists(SLA):
raise RuntimeError("SLA notice not found at {0}".format(SLA))
if not os.path.exists(eula):
raise RuntimeError("EULA notice not found at {0}".format(eula))
with open(SLA, "rb") as sla_text:
html = sla_text.read()
with open(eula, "rb") as eula_text:
html = eula_text.read()
unicode = html.decode("windows-1252")
encoded_str = unicode.encode("ascii", "xmlcharrefreplace")
read = cmds.scrollField(editable=False, wordWrap=True, height=300,
@ -388,7 +364,7 @@ def install_pkg(package):
def install_namespace_pkg(package, namespace):
"""Azure packages have issues installing one by one as the don't
"""Azure packages have issues installing one by one as they don't
unpackage correctly into the namespace directory. So we have to install
to a temp directory and move it to the right place.
@ -418,19 +394,19 @@ def install_namespace_pkg(package, namespace):
def initializePlugin(obj):
"""Initialize Plug-in"""
print("Initializing Azure Batch plug-in")
existing = cmds.optionVar(exists=SLA_PREF)
existing = cmds.optionVar(exists=EULA_PREF)
if not existing:
agree = cmds.layoutDialog(ui=sla_prompt, title="Azure Batch Maya Client")
agree = cmds.layoutDialog(ui=eula_prompt, title="Azure Batch Maya Client")
if str(agree) != 'Agree':
raise RuntimeError("Plugin initialization aborted.")
cmds.optionVar(stringValue=(SLA_PREF, VERSION))
cmds.optionVar(stringValue=(EULA_PREF, VERSION))
else:
agreed = cmds.optionVar(query=SLA_PREF)
agreed = cmds.optionVar(query=EULA_PREF)
if StrictVersion(agreed) < VERSION:
agree = cmds.layoutDialog(ui=sla_prompt, title="AzureBatch Maya Client")
agree = cmds.layoutDialog(ui=eula_prompt, title="Azure Batch Maya Client")
if str(agree) != 'Agree':
raise RuntimeError("Plugin initialization aborted.")
cmds.optionVar(stringValue=(SLA_PREF, VERSION))
cmds.optionVar(stringValue=(EULA_PREF, VERSION))
print("Checking for dependencies...")
missing_libs = []
@ -483,7 +459,7 @@ def initializePlugin(obj):
raise ImportError("Please restart Maya. Azure Batch installed "
"Python dependencies.")
print("Dependency check complete!")
print("Dependency check complete")
plugin = OpenMayaMPx.MFnPlugin(
obj, "Microsoft Corporation", VERSION, "Any")
plugin.registerCommand(cmd_name, cmd_creator)
@ -507,7 +483,7 @@ def initializePlugin(obj):
def uninitializePlugin(obj):
"""Remove and uninstall plugin."""
print("Removing AzureBatch plug-in")
print("Removing Azure Batch plug-in")
plugin = MFnPlugin(obj)
plugin.deregisterCommand(cmd_name)
try:
@ -520,7 +496,7 @@ def uninitializePlugin(obj):
if cmds.window("AzureBatch", exists=1):
cmds.deleteUI("AzureBatch")
AzureBatchSetup.remove_environment()
print("Finished clearing up all AzureBatch components")
print("Finished clearing up all Azure Batch components")
"""Check for environment and set up if not found."""
@ -528,5 +504,5 @@ try:
sys.path.extend(os.environ["AZUREBATCH_SCRIPTS"].split(os.pathsep))
sys.path.append(os.environ['AZUREBATCH_MODULES'])
except KeyError as e:
print("Couldn't find AzureBatch environment, setting up now...")
print("Couldn't find Azure Batch environment, setting up now...")
setup_module()

Просмотреть файл

Просмотреть файл

@ -1,30 +1,7 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
try:
from maya import cmds, mel, utils
@ -32,7 +9,6 @@ try:
import maya.OpenMayaMPx as omp
except ImportError:
print("No maya module found.")
import os
import logging
LOG = logging.getLogger('AzureBatchMaya')
@ -331,6 +307,14 @@ class MayaAPI(object):
LOG.debug("MayaAPI exception in 'int_slider': {0}".format(exp).strip())
return None
@staticmethod
def int_field(*args, **kwargs):
try:
return cmds.intField(*args, **kwargs)
except Exception as exp:
LOG.debug("MayaAPI exception in 'int_field': {0}".format(exp).strip())
return None
@staticmethod
def popup_menu(*args, **kwargs):
try:

Просмотреть файл

@ -1,30 +1,7 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import logging
from datetime import datetime
@ -52,15 +29,16 @@ from default import AzureBatchRenderAssets
SYS_SEARCHPATHS = []
USR_SEARCHPATHS = []
UPLOAD_THREADS = 10
BYTES = 1024
class AzureBatchAssets(object):
"""Handler for asset file functionality."""
def __init__(self, frame, call):
def __init__(self, index, frame, call):
"""Create new Asset Handler.
:param index: The UI tab index.
:param frame: The shared plug-in UI frame.
:type frame: :class:`.AzureBatchUI`
:param func call: The shared REST API call wrapper.
@ -69,6 +47,8 @@ class AzureBatchAssets(object):
self._call = call
self._session = None
self._assets = None
self._tab_index = index
self._upload_threads = None
self.batch = None
self.modules = self._collect_modules()
@ -154,6 +134,12 @@ class AzureBatchAssets(object):
self.renderer = AzureBatchRenderAssets()
self._log.debug("Configured renderer to {0}".format(self.renderer.render_engine))
def _switch_tab(self):
"""Make this tab the currently displayed tab. If this tab is already
open, this will do nothing.
"""
self.frame.select_tab(self._tab_index)
def _collect_assets(self):
"""Called on upload. If the asset tab has not yet been loaded before
job submission is attempted, then the asset references have not yet
@ -174,7 +160,7 @@ class AzureBatchAssets(object):
for rule in maya.workspace(fileRuleList=True):
project_dir = maya.workspace(fileRuleEntry=rule)
remote_path = utils.get_remote_directory(maya.workspace(en=project_dir), os_flavor)
if os_flavor == 'Windows':
if os_flavor == utils.OperatingSystem.windows:
full_remote_path = "X:\\\\" + remote_path
else:
full_remote_path = "/X/" + remote_path
@ -203,7 +189,7 @@ class AzureBatchAssets(object):
handle.write("loadPlugin \"{}\";\n".format(plugin))
handle.write("dirmap -en true;\n")
for local, remote in pathmap.items():
if os_flavor == 'Windows':
if os_flavor == utils.OperatingSystem.windows:
full_remote_path = "X:\\\\" + remote(os_flavor)
else:
full_remote_path = "/X/" + remote(os_flavor)
@ -213,18 +199,19 @@ class AzureBatchAssets(object):
return Asset(map_file, [], self.batch, self._log)
def _upload_all(self, to_upload, progress, total, project):
"""Upload all selected assets in 10 threads."""
"""Upload all selected assets in configured number of threads."""
uploads_running = []
progress_queue = Queue()
for i in range(0, len(to_upload), UPLOAD_THREADS):
for index, asset in enumerate(to_upload[i:i + UPLOAD_THREADS]):
threads = self._upload_threads()
self._log.debug("Uploading assets in {} threads.".format(threads))
for i in range(0, len(to_upload), threads):
for index, asset in enumerate(to_upload[i:i + threads]):
self._log.debug("Starting thread for asset: {}".format(asset.path))
upload = threading.Thread(
target=asset.upload, args=(index, progress, progress_queue, project))
upload.start()
uploads_running.append(upload)
self._log.debug("Batch of asset uploads pending: {}".format(threading.active_count()))
while any(t for t in uploads_running if t.is_alive()) or not progress_queue.empty():
uploaded = progress_queue.get()
if isinstance(uploaded, Exception):
@ -232,18 +219,23 @@ class AzureBatchAssets(object):
elif callable(uploaded):
uploaded()
else:
total = total - (uploaded/1024/1024)
total = total - (uploaded/BYTES/BYTES)
self.ui.upload_status("Uploading {0}...".format(self._format_size(total)))
progress_queue.task_done()
def _format_size(self, data):
def _format_size(self, nbytes):
"""Format the data size in bytes to nicely display
for upload progress.
"""
if data > 1024:
return "{:0.2f}GB".format(data/1014)
else:
return "{:0.2f}MB".format(data)
suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
if nbytes == 0:
return '0 B'
i = 0
while nbytes >= BYTES and i < len(suffixes)-1:
nbytes /= BYTES
i += 1
f = ('%.2f' % nbytes).rstrip('0').rstrip('.')
return '%s %s' % (f, suffixes[i])
def _total_data(self, files):
"""Format the combined size of the files to display
@ -252,13 +244,14 @@ class AzureBatchAssets(object):
data = float(0)
for asset in files:
data += asset.size
return data/1024/1024
return data/BYTES/BYTES
def configure(self, session):
"""Populate the Batch client for the current sessions of the asset tab.
Called on successful authentication.
"""
self._session = session
self._upload_threads = session.get_threads
self.batch = self._session.batch
self._set_searchpaths()
self._assets = Assets(self.batch)
@ -288,14 +281,14 @@ class AzureBatchAssets(object):
def add_files(self, files, column_layout, scroll_layout):
"""Function called by the 'Add File(s)' button in the UI for adding arbitrary
file references to the collection that included with the next job subbmission.
file references to the collection to be included with the next job subbmission.
"""
for f in files:
self._assets.add_asset(f, self.ui, column_layout, scroll_layout)
def add_dir(self, dirs, column_layout, scroll_layout):
"""Function called by the 'Add Directory' button in the UI for adding arbitrary
file references to the collection that included with the next job subbmission.
file references to the collection to be included with the next job subbmission.
"""
for folder in dirs:
for root, _, files in os.walk(folder):
@ -310,8 +303,8 @@ class AzureBatchAssets(object):
if the upload process is part of job submission.
:param progress_bar: The progress of the current process. This is only populated
if the upload process is part of job submission.
:param job_id: The ID of the job being submitted. This is only populated is the
upload process if path of job submission.
:param job_id: The ID of the job being submitted. This is only populated if the
upload process is part of job submission.
:param load_plugins: A list of plugins to be added to the pre-render script for
loading on the server. Only populated if part of a job submission.
:param os_flavor: The OS flavor of the rendering pool. Only set as part of the job
@ -339,7 +332,7 @@ class AzureBatchAssets(object):
progress_bar.is_cancelled()
progress_bar.status('Uploading files...')
progress_bar.max(len(asset_refs))
self.frame.select_tab(3)
self._switch_tab()
self.ui.disable(False)
self.ui.upload_button.start()
payload = self._total_data(asset_refs)
@ -364,7 +357,7 @@ class AzureBatchAssets(object):
else:
maya.error(str(exp))
finally:
# If part of job submission errors and progress bar
# If part of job submission, errors and progress bar
# will be handled back in submission.py
if not job_set:
progress_bar.end()
@ -548,7 +541,7 @@ class Assets(object):
class Asset(object):
"""Representation of a single asset, managing it's file reference,
"""Representation of a single asset, managing its file reference,
display listing and upload of the file.
"""
@ -652,7 +645,7 @@ class Asset(object):
def make_visible(self, index):
"""Attempt to auto-scroll the asset display list so that the progress of
currently uploading assets remains in view.
TODO: Thie needs some work....
TODO: This needs some work....
"""
if index == 0:
while maya.scroll_layout(self.scroll_layout, query=True, scrollAreaValue=True)[0] > 0:

Просмотреть файл

@ -226,6 +226,7 @@ class FileUtils(object):
def __init__(self, get_storage_client):
self.resource_file_cache = {}
self.container_sas_cache = {}
self.resolve_storage_account = get_storage_client
def filter_resource_cache(self, container, prefix):
@ -264,7 +265,11 @@ class FileUtils(object):
def get_container_sas(self, file_group_name):
storage_client = self.resolve_storage_account()
container = _get_container_name(file_group_name)
return _generate_container_sas_token(container, storage_client)
try:
return self.container_sas_cache[container]
except KeyError:
self.container_sas_cache[container] = _generate_container_sas_token(container, storage_client)
return self.container_sas_cache[container]
def get_container_list(self, source):
"""List blob references in container."""

Просмотреть файл

@ -5,7 +5,12 @@
from msrest.exceptions import ValidationError, ClientRequestError
from azure.batch.models import BatchErrorException
import threading
try:
from queue import Queue
except ImportError:
from Queue import Queue
# pylint: disable=too-few-public-methods
@ -25,16 +30,34 @@ def _handle_batch_exception(action):
raise Exception(ex)
def deploy_tasks(client, job_id, tasks):
def _bulk_add_tasks(client, job_id, tasks, queue):
added_tasks = client.add_collection(job_id, tasks)
for task in added_tasks.value:
queue.put(task)
def deploy_tasks(client, job_id, tasks, threads):
MAX_TASKS_COUNT_IN_BATCH = 100
submit_threads = threads or 10
def add_task():
start = 0
while start < len(tasks):
progress_queue = Queue()
submitting_tasks = []
submitted_tasks = []
while True:
end = min(start + MAX_TASKS_COUNT_IN_BATCH, len(tasks))
client.add_collection(job_id, tasks[start:end])
submit = threading.Thread(target=_bulk_add_tasks, args=(client, job_id, tasks[start:end], progress_queue))
submit.start()
submitting_tasks.append(submit)
start = end
if start >= len(tasks) or len(submitting_tasks) >= submit_threads:
while any(s for s in submitting_tasks if s.is_alive()) or not progress_queue.empty():
submitted_tasks.append(progress_queue.get())
progress_queue.task_done()
submitting_tasks = []
if start >= len(tasks):
break
return submitted_tasks
_handle_batch_exception(add_task)

Просмотреть файл

@ -436,7 +436,7 @@ def _parse_arm_parameter(name, template_obj, parameters):
user_value = user_value['value']
except TypeError:
pass
if not user_value:
if user_value is None:
raise ValueError("No value supplied for parameter '{}' and no default value".format(name))
if isinstance(user_value, dict):
# If substitute value is a complex object - it may require

Просмотреть файл

@ -106,11 +106,11 @@ class BatchExtensionsClient(BatchServiceClient):
# the Batch account in the subscription
# Example URL: https://batchaccount.westus.batch.azure.com
region = urlsplit(self.config.base_url).netloc.split('.', 2)[1]
accounts = [x for x in client.batch_account.list()
if x.name == self._account and x.location == region]
accounts = (x for x in client.batch_account.list()
if x.name == self._account and x.location == region)
try:
account = accounts[0]
except IndexError:
account = next(accounts)
except StopIteration:
raise ValueError('Couldn\'t find the account named {} in subscription {} '
'in region {}'.format(
self._account, self._subscription, region))
@ -125,7 +125,7 @@ class BatchExtensionsClient(BatchServiceClient):
keys = storage_client.storage_accounts.list_keys(storage_resource_group, storage_account)
storage_key = keys.keys[0].value # pylint: disable=no-member
self.resolved_storage_client = CloudStorageAccount(storage_account, storage_key)\
self._resolved_storage_client = CloudStorageAccount(storage_account, storage_key)\
.create_block_blob_service()
return self.resolved_storage_client
return self._resolved_storage_client

Просмотреть файл

@ -89,7 +89,7 @@ class ExtendedJobOperations(JobOperations):
except Exception as exp:
raise ValueError("Unable to deserialize to ExtendedJobParameter: {}".format(exp))
def add(self, job, job_add_options=None, custom_headers=None, raw=False, **operation_config):
def add(self, job, job_add_options=None, custom_headers=None, raw=False, threads=None, **operation_config):
"""Adds a job to the specified account.
The Batch service supports two ways to control the work done as part of
@ -176,7 +176,7 @@ class ExtendedJobOperations(JobOperations):
# Begin original job add process
result = super(ExtendedJobOperations, self).add(job, job_add_options, custom_headers, raw, **operation_config)
if task_collection:
job_utils.deploy_tasks(self._parent.task, job.id, task_collection)
job_utils.deploy_tasks(self._parent.task, job.id, task_collection, threads)
if auto_complete:
# If the option to terminate the job was set, we need to reapply it with a patch
# now that the tasks have been added.

Просмотреть файл

@ -1,35 +1,13 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import ConfigParser
import os
import logging
import sys
import traceback
from ui_config import ConfigUI
from api import MayaAPI as maya
@ -49,16 +27,20 @@ LOG_LEVELS = {
class AzureBatchConfig(object):
"""Handler for authentication and configuration of the SDK clients."""
def __init__(self, frame, start):
def __init__(self, index, frame, start):
"""Create new configuration Handler.
:param index: The UI tab index.
:param frame: The shared plug-in UI frame.
:type frame: :class:`.AzureBatchUI`
:param func call: The shared REST API call wrapper.
"""
self.ui = None
self.session = start
self._tab_index = index
self._data_dir = os.path.join(maya.prefs_dir(), 'AzureBatchData')
self._ini_file = "azure_batch.ini"
self._user_agent = "batchmaya/{}".format(os.environ.get('AZUREBATCH_VERSION'))
self._cfg = ConfigParser.ConfigParser()
self._client = None
self._log = None
@ -92,25 +74,31 @@ class AzureBatchConfig(object):
os.makedirs(self._data_dir)
config_file = os.path.join(self._data_dir, self._ini_file)
if not os.path.exists(config_file):
self._log = self._configure_logging(10)
self._log = self._configure_logging(LOG_LEVELS['debug'])
return
try:
self._cfg.read(config_file)
self._storage = storage.BlockBlobService(
self._cfg.get("AzureBatch", "storage_account"),
self._cfg.get("AzureBatch", "storage_key"),
endpoint_suffix="core.windows.net")
self._cfg.get('AzureBatch', 'storage_account'),
self._cfg.get('AzureBatch', 'storage_key'))
self._storage.MAX_SINGLE_PUT_SIZE = 2 * 1024 * 1024
credentials = SharedKeyCredentials(
self._cfg.get("AzureBatch", "batch_account"),
self._cfg.get("AzureBatch", "batch_key"))
self._cfg.get('AzureBatch', 'batch_account'),
self._cfg.get('AzureBatch', 'batch_key'))
self._client = batch.BatchExtensionsClient(
credentials, base_url=self._cfg.get("AzureBatch", "batch_url"),
credentials, base_url=self._cfg.get('AzureBatch', 'batch_url'),
storage_client=self._storage)
self._client.config.add_user_agent(self._user_agent)
self._log = self._configure_logging(
self._cfg.get("AzureBatch", "logging"))
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError) as exp:
print(exp) #TODO: Better error handling
self._cfg.get('AzureBatch', 'logging'))
except Exception as exp:
# We should only worry about this if it happens when authenticating
# using the UI, otherwise it's expected.
if self.ui:
raise ValueError("Invalid Configuration: {}".format(exp))
else:
# We'll need a place holder logger
self._log = self._configure_logging(LOG_LEVELS['debug'])
def _configure_logging(self, log_level):
"""Configure the logger. Setup the file output and format
@ -137,33 +125,37 @@ class AzureBatchConfig(object):
configuration file.
"""
try:
self._cfg.add_section("AzureBatch")
self._cfg.add_section('AzureBatch')
except ConfigParser.DuplicateSectionError:
pass
try:
self.ui.endpoint = self._cfg.get("AzureBatch", "batch_url")
self.ui.endpoint = self._cfg.get('AzureBatch', 'batch_url')
except ConfigParser.NoOptionError:
self.ui.endpoint = ""
try:
self.ui.account = self._cfg.get("AzureBatch", "batch_account")
self.ui.account = self._cfg.get('AzureBatch', 'batch_account')
except ConfigParser.NoOptionError:
self.ui.account = ""
try:
self.ui.key = self._cfg.get("AzureBatch", "batch_key")
self.ui.key = self._cfg.get('AzureBatch', 'batch_key')
except ConfigParser.NoOptionError:
self.ui.key = ""
try:
self.ui.storage = self._cfg.get("AzureBatch", "storage_account")
self.ui.storage = self._cfg.get('AzureBatch', 'storage_account')
except ConfigParser.NoOptionError:
self.ui.storage = ""
try:
self.ui.storage_key = self._cfg.get("AzureBatch", "storage_key")
self.ui.storage_key = self._cfg.get('AzureBatch', 'storage_key')
except ConfigParser.NoOptionError:
self.ui.storage_key = ""
try:
self.ui.logging = int(self._cfg.get("AzureBatch", "logging"))
self.ui.logging = self._cfg.getint('AzureBatch', 'logging')
except ConfigParser.NoOptionError:
self.ui.logging = 10
try:
self.ui.threads = self._cfg.getint('AzureBatch', 'threads')
except ConfigParser.NoOptionError:
self.ui.threads = 20
self.ui.set_authenticate(self._auth)
def _auto_authentication(self):
@ -172,82 +164,95 @@ class AzureBatchConfig(object):
"""
try:
filter = batch.models.PoolListOptions(max_results=1, select="id")
self._client.pool.list(filter)
self._storage.create_container("batch-maya-assets", fail_on_exist=False)
list(self._client.pool.list(filter))
self._storage.list_containers(num_results=1)
return True
except Exception as exp:
self._log.info("Could not get authenticate session: {0}".format(exp))
self._log.info("Failed to authenticate: {0}".format(exp))
return False
def _save_config(self):
"""Persist the current plugin configuration to file."""
config_file = os.path.join(self._data_dir, self._ini_file)
with open(config_file, 'w') as handle:
self._cfg.write(handle)
def set_logging(self, level):
"""Set the logging level to that specified in the UI.
:param str level: The specified logging level.
"""
log_level = int(LOG_LEVELS[level])
self._log.setLevel(log_level)
self._cfg.set("AzureBatch", "logging", str(level))
self._log.setLevel(level)
self._cfg.set('AzureBatch', 'logging', level)
self._save_config()
def set_threads(self, threads):
"""Set the number of threads to that specified in the UI.
:param int threads: The specified number of threads.
"""
self._cfg.set('AzureBatch', 'threads', threads)
self._save_config()
def save_changes(self):
"""Persist configuration changes to file for future sessions."""
"""Persist auth config changes to file for future sessions."""
try:
self._cfg.add_section("AzureBatch")
self._cfg.add_section('AzureBatch')
except ConfigParser.DuplicateSectionError:
pass
self._cfg.set("AzureBatch", "batch_url", self.ui.endpoint)
self._cfg.set("AzureBatch", "batch_account", self.ui.account)
self._cfg.set("AzureBatch", "batch_key", self.ui.key)
self._cfg.set("AzureBatch", "storage_account", self.ui.storage)
self._cfg.set("AzureBatch", "storage_key", self.ui.storage_key)
self._cfg.set("AzureBatch", "logging", self.ui.logging)
config_file = os.path.join(self._data_dir, self._ini_file)
with open(config_file, 'w') as handle:
self._cfg.write(handle)
self._cfg.set('AzureBatch', 'batch_url', self.ui.endpoint)
self._cfg.set('AzureBatch', 'batch_account', self.ui.account)
self._cfg.set('AzureBatch', 'batch_key', self.ui.key)
self._cfg.set('AzureBatch', 'storage_account', self.ui.storage)
self._cfg.set('AzureBatch', 'storage_key', self.ui.storage_key)
self._save_config()
def authenticate(self):
"""Begin authentication - initiated by the UI button."""
self._configure_plugin()
self._auth = self._auto_authentication()
self.ui.set_authenticate(self._auth)
self.session()
try:
self._configure_plugin()
self._auth = self._auto_authentication()
except ValueError as exp:
maya.error(str(exp))
self._auth = False
finally:
self.ui.set_authenticate(self._auth)
self.session()
def get_threads(self):
"""Attempt to retrieve number of threads configured for the plugin."""
return self.ui.threads
def get_cached_vm_sku(self):
"""Attempt to retrieve a selected VM SKU from a previous session."""
try:
return self._cfg.get("AzureBatch", "vm_sku")
return self._cfg.get('AzureBatch', 'vm_sku')
except ConfigParser.NoOptionError:
return None
def store_vm_sku(self, sku):
"""Cache selected VM SKU for later sessions."""
self._cfg.set("AzureBatch", "vm_sku", sku)
config_file = os.path.join(self._data_dir, self._ini_file)
with open(config_file, 'w') as handle:
self._cfg.write(handle)
self._cfg.set('AzureBatch', 'vm_sku', sku)
self._save_config()
def get_cached_image(self):
"""Attempt to retrieve a selected image a previous session."""
try:
return self._cfg.get("AzureBatch", "image")
return self._cfg.get('AzureBatch', 'image')
except ConfigParser.NoOptionError:
return None
def store_image(self, image):
"""Cache selected image for later sessions."""
self._cfg.set("AzureBatch", "image", image)
config_file = os.path.join(self._data_dir, self._ini_file)
with open(config_file, 'w') as handle:
self._cfg.write(handle)
self._cfg.set('AzureBatch', 'image', image)
self._save_config()
def get_cached_autoscale_formula(self):
"""Attempt to retrieve an autoscale forumla from a previous session."""
try:
return self._cfg.get("AzureBatch", "autoscale")
return self._cfg.get('AzureBatch', 'autoscale')
except ConfigParser.NoOptionError:
return None
def store_autoscale_formula(self, formula):
"""Cache selected VM SKU for later sessions."""
self._cfg.set("AzureBatch", "autoscale", formula)
config_file = os.path.join(self._data_dir, self._ini_file)
with open(config_file, 'w') as handle:
self._cfg.write(handle)
self._cfg.set('AzureBatch', 'autoscale', formula)
self._save_config()

Просмотреть файл

@ -1,30 +1,7 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import os
import logging
@ -32,7 +9,7 @@ import json
from api import MayaAPI as maya
from api import MayaCallbacks as callback
import utils
from ui_environment import EnvironmentUI
@ -41,16 +18,16 @@ MAYA_IMAGES = {
{
'node_sku_id': 'batch.node.windows amd64',
'publisher': 'batch',
'offer': 'autodesk-maya-arnold-win2016-preview',
'sku': 'maya2017',
'offer': 'rendering-windows2016',
'sku': 'rendering',
'version': 'latest'
},
'Batch CentOS Preview':
{
'node_sku_id': 'batch.node.centos 7',
'publisher': 'batch',
'offer': 'autodesk-maya-arnold-centos73-preview',
'sku': 'maya2017',
'offer': 'autodesk-maya-arnold-centos73',
'sku': 'maya-arnold-2017',
'version': 'latest'
},
}
@ -63,9 +40,10 @@ LICENSES = [
class AzureBatchEnvironment(object):
"""Handler for rendering environment configuration functionality."""
def __init__(self, frame, call):
def __init__(self, index, frame, call):
"""Create new Environment Handler.
:param index: The UI tab index.
:param frame: The shared plug-in UI frame.
:type frame: :class:`.AzureBatchUI`
:param func call: The shared REST API call wrapper.
@ -73,6 +51,7 @@ class AzureBatchEnvironment(object):
self._log = logging.getLogger('AzureBatchMaya')
self._call = call
self._session = None
self._tab_index = index
self.licenses = {}
self._get_plugin_licenses()
@ -141,8 +120,8 @@ class AzureBatchEnvironment(object):
if pool_image:
return pool_image[0]
else:
self._log.debug("Pool using unknown image reference: {}".format(image_ref['offer']))
return ""
self._log.debug("Pool using unknown image reference: {}".format(image_ref.offer))
return image_ref.offer
def get_vm_sku(self):
return self.ui.get_sku()
@ -152,16 +131,16 @@ class AzureBatchEnvironment(object):
windows_offers = [value['offer'] for value in MAYA_IMAGES.values() if 'windows' in value['node_sku_id']]
linux_offers = [value['offer'] for value in MAYA_IMAGES.values() if value['offer'] not in windows_offers]
if pool_image.offer in windows_offers:
return 'Windows'
return utils.OperatingSystem.windows
elif pool_image.offer in linux_offers:
return 'Linux'
return utils.OperatingSystem.linux
else:
raise ValueError('Selected pool is not using a valid Maya image.')
if 'Windows' in self.ui.get_image():
return 'Windows'
if utils.OperatingSystem.windows.value in self.ui.get_image():
return utils.OperatingSystem.windows
else:
return 'Linux'
return utils.OperatingSystem.linux
def get_environment_settings(self):
env_vars = self.ui.get_env_vars()

Просмотреть файл

@ -1,30 +1,7 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import traceback
import logging

Просмотреть файл

@ -1,30 +1,7 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import os
import logging
@ -37,17 +14,18 @@ import shutil
import re
from api import MayaAPI as maya
from ui_history import HistoryUI
from ui_jobhistory import JobHistoryUI
import azure.batch as batch
class AzureBatchHistory(object):
class AzureBatchJobHistory(object):
"""Handler for job display functionality."""
def __init__(self, frame, call):
def __init__(self, index, frame, call):
"""Create new job history Handler.
:param index: The UI tab index.
:param frame: The shared plug-in UI frame.
:type frame: :class:`.AzureBatchUI`
:param func call: The shared REST API call wrapper.
@ -55,13 +33,14 @@ class AzureBatchHistory(object):
self._log = logging.getLogger('AzureBatchMaya')
self._call = call
self._session = None
self._tab_index = index
self.batch = None
self.index = 0
self.per_call = 5
self.jobs_per_page = 5
self.count = 0
self.min = True
self.max = False
self.ui = HistoryUI(self, frame)
self.ui = JobHistoryUI(self, frame)
self.all_jobs = []
self.jobs = []
self.selected_job = None
@ -70,17 +49,28 @@ class AzureBatchHistory(object):
"""Get the pixel height of the job thumbnail to display.
Note: This function only works under Python 2.7
"""
# The first 8 bytes of data are the PNG signature, the next 4
# are the type field of the first chunk (which we don't need).
png_signature_bytes = 12
# The IHDR header is always the first chunk in a valid PNG image
png_header = 'IHDR'
# X and Y image dimensions are the first 8 bytes of the IDHR chunk
dimensions_bytes = 8
# 120 pixels is the default height of a thumbnail image
y = 120
# Byte order = network (!), format = unsigned long (L)
excepted_byte_format = '!LL'
with open(image, 'rb') as im:
im.read(12)
if im.read(4) == 'IHDR':
x, y = struct.unpack("!LL", im.read(8))
png_signature = im.read(png_signature_bytes)
valid_png = im.read(len(png_header)) == png_header
if valid_png:
x, y = struct.unpack(excepted_byte_format, im.read(dimensions_bytes))
return y
def _download_thumbnail(self, job, thumbs):
"""Download a preview thumbnail for the selected job.
Only certain output formats are supported. If not thumbnail exists
then we display the default 'no preview' image.
Only certain output formats are supported. If the thumbnail doesn't
exist then we display the default 'no preview' image.
TODO: Remove direct storage reference to use batch.download.
:param job: The selected job object.
@ -105,7 +95,7 @@ class AzureBatchHistory(object):
self._log.debug("Thumbnail path: {}".format(thumb_path))
try:
if not os.path.isfile(thumb_path):
self._log.info("Downloading task thumb: {}".format(thumbs[-1]))
self._log.info("Downloading task thumbnail: {}".format(thumbs[-1]))
self.storage.get_blob_to_path('fgrp-' + job.id, thumbs[-1], thumb_path)
self._log.info(" thumbnail download successful.\n")
except Exception as exp:
@ -120,25 +110,25 @@ class AzureBatchHistory(object):
"""Calculate the paging progress label, including which page
is currently displayed out of how many.
"""
if (self.index + self.per_call) > self.count:
extra = (self.index + self.per_call) - self.count
new_range = ((self.index + self.per_call) - extra)
if (self.index + self.jobs_per_page) > self.count:
extra = (self.index + self.jobs_per_page) - self.count
new_range = ((self.index + self.jobs_per_page) - extra)
self.ui.num_jobs = "{0} - {1} of {2}".format(
min((self.index + 1), new_range), new_range, self.count)
else:
self.ui.num_jobs = "{0} - {1} of {2}".format(
min((self.index + 1), self.count), (self.index + self.per_call), self.count)
min((self.index + 1), self.count), (self.index + self.jobs_per_page), self.count)
def _set_min_max(self):
"""Determine whether we are currently displaying the first or
last page, for that the forward and back buttons can be disabled
last page, so that the forward and back buttons can be disabled
accordingly.
"""
self.min = True if self.index < 1 else False
if (self.count % self.per_call) == 0:
self.max = (self.index >= (self.count - self.per_call)) or (self.per_call > self.count)
if (self.count % self.jobs_per_page) == 0:
self.max = (self.index >= (self.count - self.jobs_per_page)) or (self.jobs_per_page > self.count)
else:
self.max = self.index >= (self.count - self.per_call + (self.per_call - (self.count % self.per_call)))
self.max = self.index >= (self.count - self.jobs_per_page + (self.jobs_per_page - (self.count % self.jobs_per_page)))
self.ui.last_page = not self.max
self.ui.first_page = not self.min
@ -172,7 +162,7 @@ class AzureBatchHistory(object):
def show_jobs(self):
"""Display the current page of jobs."""
self.jobs = self.all_jobs[self.index:self.index + self.per_call]
self.jobs = self.all_jobs[self.index:self.index + self.jobs_per_page]
self._set_num_jobs()
self._set_min_max()
display_jobs = []
@ -182,11 +172,11 @@ class AzureBatchHistory(object):
def show_next_jobs(self):
"""Show the next page of jobs."""
self.index = min(self.index + self.per_call, self.count)
self.index = min(self.index + self.jobs_per_page, self.count)
def show_prev_jobs(self):
"""Show the previous page of jobs."""
self.index = max(self.index - self.per_call, 0)
self.index = max(self.index - self.jobs_per_page, 0)
def show_first_jobs(self):
"""Return to the first page of jobs (most recently submitted)."""
@ -194,14 +184,14 @@ class AzureBatchHistory(object):
def show_last_jobs(self):
"""Skip to the last page of jobs (first ones submitted)."""
if (self.count % self.per_call) == 0:
self.index = self.count - self.per_call
if (self.count % self.jobs_per_page) == 0:
self.index = self.count - self.jobs_per_page
else:
self.index = self.count - self.per_call + \
(self.per_call - (self.count % self.per_call))
self.index = self.count - self.jobs_per_page + \
(self.jobs_per_page - (self.count % self.jobs_per_page))
def job_selected(self, job_ui):
"""A job has been selected, so it's details need to be retrieved
"""A job has been selected, so its details need to be retrieved
and displayed. This is also called when a job entry has been
refreshed.
"""
@ -225,6 +215,32 @@ class AzureBatchHistory(object):
self.selected_job.set_thumbnail(loading_thumb, 24)
maya.refresh()
job = self._call(self.batch.job.get, job.id)
self.selected_job.set_status('loading...')
self.selected_job.set_progress('loading...')
self.selected_job.set_tasks('loading...')
self.selected_job.set_submission(job.creation_time.isoformat())
self.selected_job.set_job(job.id)
self.selected_job.set_pool(job.pool_info.pool_id)
self.selected_job.set_label(job.display_name)
maya.refresh()
self._log.info("Updated {0}".format(job.display_name))
except Exception as exp:
self._log.warning("Failed to update job details {0}".format(exp))
self.ui.refresh()
def load_tasks(self):
"""Get a list of tasks associated with the job."""
try:
job = self.jobs[self.selected_job.index]
except (IndexError, AttributeError):
self._log.warning("Selected job index does not match jobs list.")
if not self.selected_job:
return
self.selected_job.set_status('unknown')
self.selected_job.set_progress('unknown')
self.selected_job.set_tasks('unknown')
return
try:
tasks = list(self._call(self.batch.task.list, job.id))
completed_tasks = [t for t in tasks if t.state == batch.models.TaskState.completed]
errored_tasks = [t for t in completed_tasks if t.execution_info.exit_code != 0]
@ -235,18 +251,13 @@ class AzureBatchHistory(object):
else:
percentage = (100 * len(completed_tasks)) / (len(tasks))
self.selected_job.set_status(state)
self.selected_job.set_progress(percentage)
self.selected_job.set_submission(job.creation_time.isoformat())
self.selected_job.set_progress(str(percentage)+'%')
self.selected_job.set_tasks(len(tasks))
self.selected_job.set_job(job.id)
self.selected_job.set_pool(job.pool_info.pool_id)
self.selected_job.set_label(job.display_name)
maya.refresh()
self._log.info("Updated {0}".format(job.display_name))
except Exception as exp:
self._log.warning("Failed to update job details {0}".format(exp))
self.ui.refresh()
def get_thumbnail(self):
"""Check job outputs of the currently selected job to find
any available thumbnails.
@ -267,7 +278,6 @@ class AzureBatchHistory(object):
self._log.warning(exp)
blobs = []
thumbs = sorted([b.name for b in blobs])
print(thumbs)
self._download_thumbnail(job, thumbs)
def cancel_job(self):
@ -276,6 +286,7 @@ class AzureBatchHistory(object):
job = self.jobs[self.selected_job.index]
self._call(self.batch.job.terminate, job.id)
self.update_job(self.selected_job.index)
maya.execute(self.load_tasks)
maya.execute(self.get_thumbnail)
maya.refresh()
except (IndexError, AttributeError) as exp:
@ -291,6 +302,7 @@ class AzureBatchHistory(object):
job = self.jobs[self.selected_job.index]
self._call(self.batch.job.delete, job.id)
self.update_job(self.selected_job.index)
maya.execute(self.load_tasks)
maya.execute(self.get_thumbnail)
maya.refresh()
except (IndexError, AttributeError) as exp:

Просмотреть файл

@ -1,30 +1,7 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import os
import logging
@ -42,9 +19,10 @@ from ui_pools import PoolsUI
class AzureBatchPools(object):
"""Handler for pool functionality."""
def __init__(self, frame, call):
def __init__(self, index, frame, call):
"""Create new Pool Handler.
:param index: The UI tab index.
:param frame: The shared plug-in UI frame.
:type frame: :class:`.AzureBatchUI`
:param func call: The shared REST API call wrapper.
@ -52,6 +30,7 @@ class AzureBatchPools(object):
self._log = logging.getLogger('AzureBatchMaya')
self._call = call
self._session = None
self._tab_index = index
self.batch = None
self.ui = PoolsUI(self, frame)
@ -77,10 +56,15 @@ class AzureBatchPools(object):
"""
#if lazy and self.pools:
# return [pool.id for pool in self.pools if not pool.auto]
self.pools = [p for p in self._call(self.batch.pool.list) if p.id.startswith('Maya_')]
all_pools = self._call(self.batch.pool.list)
self.pools = []
for pool in all_pools:
if pool.virtual_machine_configuration and \
pool.virtual_machine_configuration.image_reference.publisher == 'batch':
self.pools.append(pool)
self.pools.sort(key=lambda x: x.creation_time, reverse=True)
self.count = len(self.pools)
return [pool.id for pool in self.pools if pool.id.startswith("Maya_Pool")]
return [pool.id for pool in self.pools if not pool.id.startswith("Maya_Auto_Pool")]
def get_pools(self):
"""Retrieves the currently running pools and populates the UI
@ -114,13 +98,13 @@ class AzureBatchPools(object):
pool = self._call(self.batch.pool.get, pool.id)
_nodes = self._call(self.batch.compute_node.list, pool.id)
nodes = [n for n in _nodes]
self.selected_pool.set_id(pool.id)
self.selected_pool.set_label(pool.display_name if pool.display_name else pool.id)
self.selected_pool.set_size(pool.current_dedicated_nodes)
self.selected_pool.set_target(pool.target_dedicated_nodes)
self.selected_pool.set_dedicated_size(pool)
self.selected_pool.set_low_pri_size(pool)
self.selected_pool.set_type(
"Auto" if pool.id.startswith("Maya_Auto_Pool") else "Provisioned")
self.selected_pool.set_state(pool.state.value, nodes)
self.selected_pool.set_tasks(pool.max_tasks_per_node)
self.selected_pool.set_allocation(pool.allocation_state.value)
self.selected_pool.set_created(pool.creation_time)
self.selected_pool.set_licenses(pool.application_licenses)
@ -158,10 +142,10 @@ class AzureBatchPools(object):
"""Get the target number of VMs in the selected pool."""
try:
pool = self.pools[self.selected_pool.index]
return int(pool.target_dedicated_nodes)
return int(pool.target_dedicated_nodes), int(pool.target_low_priority_nodes)
except (IndexError, TypeError, AttributeError) as exp:
self._log.info("Failed to parse pool target size {0}".format(exp))
return 0
return 0, 0
def get_pool_os(self, pool_id):
"""Get the OS flavor of the specified pool ID."""
@ -190,7 +174,8 @@ class AzureBatchPools(object):
application_licenses=self.environment.get_application_licenses(),
vm_size=self.environment.get_vm_sku(),
virtual_machine_configuration=pool_config,
target_dedicated_nodes=int(size),
target_dedicated_nodes=int(size[0]),
target_low_priority_nodes=int(size[1]),
max_tasks_per_node=1)
self._call(self.batch.pool.add, new_pool)
self._log.debug("Successfully created pool.")
@ -211,7 +196,8 @@ class AzureBatchPools(object):
'virtualMachineConfiguration': pool_config,
'maxTasksPerNode': 1,
'applicationLicenses': self.environment.get_application_licenses(),
'targetDedicatedNodes': int(size)}
'targetDedicatedNodes': int(size[0]),
'targetLowPriorityNodes': int(size[1])}
auto_pool = {
'autoPoolIdPrefix': "Maya_Auto_Pool_",
'poolLifetimeOption': "job",
@ -219,13 +205,16 @@ class AzureBatchPools(object):
'pool': pool_spec}
return {'autoPoolSpecification': auto_pool}
def resize_pool(self, new_size):
def resize_pool(self, new_dedicated, new_low_pri):
"""Resize an existing pool."""
try:
pool = self.pools[self.selected_pool.index]
self._log.info("Resizing pool '{}' to {} VMs".format(pool.id, new_size))
self._call(self.batch.pool.resize, pool.id, {'target_dedicated_nodes':int(new_size),
'target_low_priority_nodes': 0})
self._log.info(
"Resizing pool '{}' to {} dedicated VMs"
" and {} low priority VMs".format(pool.id, new_dedicated, new_low_pri))
self._call(self.batch.pool.resize, pool.id,
{'target_dedicated_nodes':int(new_dedicated),
'target_low_priority_nodes': int(new_low_pri)})
maya.refresh()
except Exception as exp:
self._log.info("Failed to resize pool {0}".format(exp))

Просмотреть файл

@ -1,40 +1,17 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import logging
import webbrowser
import os
import threading
import sys
import traceback
from ui_shared import AzureBatchUI
from config import AzureBatchConfig
from submission import AzureBatchSubmission
from history import AzureBatchHistory
from jobhistory import AzureBatchJobHistory
from assets import AzureBatchAssets
from pools import AzureBatchPools
from environment import AzureBatchEnvironment
@ -51,6 +28,15 @@ ACCEPTED_ERRORS = [
class AzureBatchSettings(object):
tab_index = {
'AUTH': 1,
'SUBMIT': 2,
'ASSETS': 3,
'POOLS': 4,
'JOBHISTORY': 5,
'ENV': 6
}
@staticmethod
def starter():
"""Called by the mel script when the shelf button is clicked."""
@ -63,30 +49,31 @@ class AzureBatchSettings(object):
self._log = logging.getLogger('AzureBatchMaya')
try:
self.frame = AzureBatchUI(self)
self.config = AzureBatchConfig(self.frame, self.start)
self.submission = AzureBatchSubmission(self.frame, self.call)
self.assets = AzureBatchAssets(self.frame, self.call)
self.pools = AzureBatchPools(self.frame, self.call)
self.history = AzureBatchHistory(self.frame, self.call)
self.env = AzureBatchEnvironment(self.frame, self.call)
self.config = AzureBatchConfig(self.tab_index['AUTH'], self.frame, self.start)
self.submission = AzureBatchSubmission(self.tab_index['SUBMIT'], self.frame, self.call)
self.assets = AzureBatchAssets(self.tab_index['ASSETS'], self.frame, self.call)
self.pools = AzureBatchPools(self.tab_index['POOLS'], self.frame, self.call)
self.jobhistory = AzureBatchJobHistory(self.tab_index['JOBHISTORY'], self.frame, self.call)
self.env = AzureBatchEnvironment(self.tab_index['ENV'], self.frame, self.call)
self.start()
except Exception as exp:
if (maya.window("AzureBatch", q=1, exists=1)):
maya.delete_ui("AzureBatch")
message = "Batch Plugin Failed to Start: {0}".format(exp)
maya.error(message)
raise
def start(self):
"""Start the plugin UI. Depending on whether auto-authentication was
successful, the plugin will start by displaying the submission tab.
Otherwise the UI will be disables, and the log in tab will be displayed.
Otherwise the UI will be disabled, and the login tab will be displayed.
"""
try:
self._log.debug("Starting AzureBatchShared...")
if self.config.auth:
self.frame.is_logged_in()
self.env.configure(self.config)
self.history.configure(self.config)
self.jobhistory.configure(self.config)
self.assets.configure(self.config)
self.pools.configure(self.config, self.env)
self.submission.start(self.config, self.assets, self.pools, self.env)
@ -119,4 +106,6 @@ class AzureBatchSettings(object):
except Exception as exp:
if (maya.window("AzureBatch", q=1, exists=1)):
maya.delete_ui("AzureBatch")
exc_type, exc_value, exc_traceback = sys.exc_info()
self._log.error("".join(traceback.format_exception(exc_type, exc_value, exc_traceback)))
raise ValueError("Error: {0}".format(exp))

Просмотреть файл

@ -1,30 +1,7 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import os
import sys
@ -49,16 +26,20 @@ from default import AzureBatchRenderJob
class AzureBatchSubmission(object):
"""Handler for job submission functionality."""
def __init__(self, frame, call):
def __init__(self, index, frame, call):
"""Create new Submission Handler.
:param index: The UI tab index.
:param frame: The shared plug-in UI frame.
:type frame: :class:`.AzureBatchUI`
:param func call: The shared REST API call wrapper.
"""
self._log = logging.getLogger('AzureBatchMaya')
self._call = call
self._tab_index = index
self._submit_threads = None
self.max_pool_size = 1000
self.ui = SubmissionUI(self, frame)
self.modules = self._collect_modules()
self.renderer = None
@ -111,6 +92,12 @@ class AzureBatchSubmission(object):
self.renderer = AzureBatchRenderJob()
self._log.debug("Configured renderer to {0}".format(self.renderer.render_engine))
def _switch_tab(self):
"""Make this tab the currently displayed tab. If this tab is already
open, this will do nothing.
"""
self.frame.select_tab(self._tab_index)
def _check_outputs(self):
"""Check whether at least one of the scene cameras is marked as renderable
and that at least one layer is a render layer. If not, there will be no
@ -137,10 +124,10 @@ class AzureBatchSubmission(object):
culprits to the ignore list if necessary.
"""
try:
with open(os.path.join(os.environ["AZUREBATCH_TOOLS"],
with open(os.path.join(os.environ['AZUREBATCH_TOOLS'],
"supported_plugins.json"), 'r') as plugins:
supported_plugins = json.load(plugins)
with open(os.path.join(os.environ["AZUREBATCH_TOOLS"],
with open(os.path.join(os.environ['AZUREBATCH_TOOLS'],
"ignored_plugins.json"), 'r') as plugins:
ignored_plugins = json.load(plugins)
except EnvironmentError:
@ -154,7 +141,7 @@ class AzureBatchSubmission(object):
"yet supported.\nRendering may be affected.\n")
for plugin in unsupported_plugins:
warning += plugin + "\n"
options = ["Continue", "Cancel"]
options = ['Continue', 'Cancel']
answer = maya.confirm(warning, options)
if answer == options[-1]:
raise CancellationException("Submission Aborted")
@ -188,16 +175,16 @@ class AzureBatchSubmission(object):
pool_spec = self.ui.get_pool()
if pool_spec.get(1):
self._log.info("Using auto-pool.")
return self.pool_manager.create_auto_pool(int(pool_spec[1]), job_name)
return self.pool_manager.create_auto_pool(pool_spec[1], job_name)
if pool_spec.get(2):
self._log.info("Using existing pool.")
pool_id = str(pool_spec[2])
if pool_id == "None":
raise PoolException("No pool selected.")
return {"poolId" : pool_id}
return {'poolId' : pool_id}
if pool_spec.get(3):
self._log.info("Creating new pool.")
return self.pool_manager.create_pool(int(pool_spec[3]), job_name)
return self.pool_manager.create_pool(pool_spec[3], job_name)
def start(self, session, assets, pools, env):
"""Load submission tab after plug-in has been authenticated.
@ -217,6 +204,7 @@ class AzureBatchSubmission(object):
self.pool_manager = pools
self.env_manager = env
self.data_path = session.path
self._submit_threads = session.get_threads
if self.renderer:
self.renderer.delete()
self._configure_renderer()
@ -262,7 +250,8 @@ class AzureBatchSubmission(object):
batch_parameters['displayName'] = self.renderer.get_title()
batch_parameters['metadata'] = [{"name": "JobType", "value": "Maya"}]
template_file = os.path.join(
os.environ['AZUREBATCH_TEMPLATES'], 'arnold-basic-{}.json'.format(pool_os.lower()))
os.environ['AZUREBATCH_TEMPLATES'],
"{}-basic-{}.json".format(self.renderer.render_engine, pool_os.value.lower()))
batch_parameters['applicationTemplateInfo'] = {'filePath': template_file}
application_params = {}
batch_parameters['applicationTemplateInfo']['parameters'] = application_params
@ -280,7 +269,7 @@ class AzureBatchSubmission(object):
application_params['assetScript'] = map_url
application_params['thumbScript'] = thumb_url
application_params['workspace'] = workspace_url
self.frame.select_tab(2)
self._switch_tab()
self.ui.submit_status("Configuring job...")
progress.status("Configuring job...")
@ -298,7 +287,9 @@ class AzureBatchSubmission(object):
progress.is_cancelled()
self.ui.submit_status("Submitting...")
progress.status("Submitting...")
self._call(self.batch.job.add, new_job)
threads = self._submit_threads()
self._log.debug("Submitting using {} threads.".format(threads))
self._call(self.batch.job.add, new_job, threads=threads)
maya.info("Job submitted successfully")
if watch_job:
@ -308,10 +299,10 @@ class AzureBatchSubmission(object):
except Exception as exp:
self._log.error(str(exp))
exc_type, exc_value, exc_traceback = sys.exc_info()
self._log.debug("".join(traceback.format_exception(exc_type, exc_value, exc_traceback)))
self._log.debug(''.join(traceback.format_exception(exc_type, exc_value, exc_traceback)))
maya.error(str(exp))
finally:
if progress:
progress.end()
self.frame.select_tab(2)
self._switch_tab()
self.renderer.disable(True)

Просмотреть файл

@ -1,9 +1,8 @@
# coding=utf-8
# --------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import os
import tempfile

Просмотреть файл

@ -1,9 +1,8 @@
# coding=utf-8
# --------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import webbrowser
import ConfigParser
@ -11,16 +10,17 @@ import time
import sys
import os
import re
import threading
batch_client = None
storage_client = None
concurrent_downloads = None
header_line_length = 50
def header(header):
header_chars = len(header)
total_len = 50
dashes = total_len - header_chars
dashes = header_line_length - header_chars
mult = int(dashes/2)
padded = "\n\n" + mult*"-" + header + mult*"-"
if dashes % 2 > 0:
@ -40,22 +40,14 @@ def _check_valid_dir(directory):
def _download_output(container, blob_name, output_path, size):
def progress(data, total):
try:
percent = float(data)*100/float(size)
sys.stdout.write(' Downloading... {0}%\r'.format(int(percent)))
except:
sys.stdout.write(' Downloading... %\r')
finally:
sys.stdout.flush()
print("Downloading task output: {}".format(blob_name))
storage_client.get_blob_to_path(container, blob_name, output_path, progress_callback=progress)
print(" Output download successful.\n")
storage_client.get_blob_to_path(container, blob_name, output_path)
print("Output {} download successful".format(blob_name))
def _track_completed_outputs(container, dwnld_dir):
job_outputs = storage_client.list_blobs(container)
downloads = []
for output in job_outputs:
if output.name.startswith('thumbs/'):
continue
@ -64,7 +56,17 @@ def _track_completed_outputs(container, dwnld_dir):
if not os.path.isfile(output_file):
if not os.path.isdir(os.path.dirname(output_file)):
os.makedirs(os.path.dirname(output_file))
_download_output(container, output.name, output_file, output.properties.content_length)
downloads.append(
threading.Thread(
target=_download_output,
args=(container, output.name, output_file, output.properties.content_length)))
downloads[-1].start()
if len(downloads) >= concurrent_downloads:
for thread in downloads:
thread.join()
downloads = []
for thread in downloads:
thread.join()
def _check_job_stopped(job):
@ -88,7 +90,7 @@ def _check_job_stopped(job):
if job.state in stopped_status:
print(header("Job has stopped"))
print("Job status: {0}".format(job.state))
raise RuntimeError("Job is no longer running. Status: {0}".format(job.state))
raise RuntimeError("Job is no longer active. State: {0}".format(job.state))
elif job.state == JobState.completed:
print(header("Job has completed"))
return True
@ -115,7 +117,7 @@ def track_job_progress(id, container, dwnld_dir):
percentage = (100 * len(completed_tasks)) / len(tasks)
print("Running - {}%".format(percentage))
if errored_tasks:
print(" - Warning: some tasks have completed with a non-zero exit code.")
print(" - Warning: some tasks have failed.")
_track_completed_outputs(container, dwnld_dir)
if _check_job_stopped(job):
@ -129,7 +131,7 @@ def track_job_progress(id, container, dwnld_dir):
def _authenticate(cfg_path):
global batch_client, storage_client
global batch_client, storage_client, concurrent_downloads
cfg = ConfigParser.ConfigParser()
try:
cfg.read(cfg_path)
@ -142,6 +144,10 @@ def _authenticate(cfg_path):
cfg.get("AzureBatch", "storage_account"),
cfg.get("AzureBatch", "storage_key"),
endpoint_suffix="core.windows.net")
try:
concurrent_downloads = cfg.get("AzureBatch", "threads")
except ConfigParser.NoSectionError:
concurrent_downloads = 20
except (EnvironmentError, ConfigParser.NoOptionError, ConfigParser.NoSectionError) as exp:
raise ValueError("Failed to authenticate using Maya configuration {0}".format(cfg_path))

Просмотреть файл

@ -1,30 +1,7 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import os
import utils

Просмотреть файл

@ -1,30 +1,7 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import utils
@ -60,6 +37,17 @@ class ConfigUI(object):
maya.text(label="Service: ", align="left")
self._endpoint = maya.text_field(height=25, enable=True)
#TODO: Allow set to 0 to disable threads
with utils.Row(2, 2, (70,260), ("left","left")):
maya.text(label="Threads: ", align="left")
self._threads = maya.int_field(
changeCommand=self.set_threads,
height=25,
minValue=1,
maxValue=40,
enable=True,
value=20)
with utils.Row(2, 2, (70,260), ("left","center"),
[(1, "bottom", 20),(2,"bottom",15)]):
maya.text(label="Logging: ", align="left")
@ -116,6 +104,16 @@ class ConfigUI(object):
"""AzureBatch Service Endpoint. Sets contents of text field."""
maya.text_field(self._endpoint, edit=True, text=str(value))
@property
def threads(self):
"""Max number of threads used. Retrieves contents of int field."""
return maya.int_field(self._threads, query=True, value=True)
@threads.setter
def threads(self, value):
"""Max number of threads used. Sets contents of iny field."""
maya.int_field(self._threads, edit=True, value=int(value))
@property
def account(self):
"""AzureBatch Unattended Account ID. Retrieves contents of text field."""
@ -164,7 +162,10 @@ class ConfigUI(object):
@status.setter
def status(self, value):
"""Plug-in authentication status. Sets contents of label."""
maya.text(self.auth_status, edit=True, label=value)
if value:
maya.text(self.auth_status, edit=True, label="Authenticated", backgroundColor=[0.23, 0.44, 0.21])
else:
maya.text(self.auth_status, edit=True, label="Not authenticated", backgroundColor=[0.6, 0.23, 0.23])
@property
def logging(self):
@ -198,12 +199,19 @@ class ConfigUI(object):
"""Set logging level. Command for logging dropdown selection.
:param str level: The selected logging level, e.g. ``debug``.
"""
self.base.set_logging(level.lower())
self.base.set_logging(self.logging)
def set_threads(self, threads):
"""Set number of threads. OnChange command for threads field.
:param int threads: The selected number of threads.
"""
self.base.set_threads(int(threads))
def set_authenticate(self, auth):
"""Set label of authentication button depending on auth status.
:param bool auth: Whether plug-in is authenticated.
"""
self.status = auth
if auth:
maya.button(
self._authenticate, edit=True, label="Refresh Authentication")

Просмотреть файл

@ -1,30 +1,7 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import os
import utils

Просмотреть файл

@ -1,30 +1,7 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import os
import webbrowser
@ -33,14 +10,14 @@ from api import MayaAPI as maya
import utils
class HistoryUI(object):
class JobHistoryUI(object):
"""Class to create the 'Jobs' tab in the plug-in UI"""
def __init__(self, base, frame):
"""Create 'Jobs' tab and add to UI frame.
:param base: The base class for handling jobs monitoring functionality.
:type base: :class:`.AzureBatchHistory`
:type base: :class:`.AzureBatchJobHistory`
:param frame: The shared plug-in UI frame.
:type frame: :class:`.AzureBatchUI`
"""
@ -260,7 +237,7 @@ class AzureBatchJobInfo(object):
"""Create a new job reference.
:param base: The base class for handling jobs monitoring functionality.
:type base: :class:`.AzureBatchHistory`
:type base: :class:`.AzureBatchJobHistory`
:param int index: The index of where this reference is displayed on
the current page.
:param layout: The layout on which the job details will be displayed.
@ -308,7 +285,7 @@ class AzureBatchJobInfo(object):
"""Set the label for progress complete.
:param int value: The percent complete.
"""
maya.text(self._progress, edit=True, label=" {0}%".format(value))
maya.text(self._progress, edit=True, label=" {0}".format(value))
def set_submission(self, value):
"""Set the label for date/time submitted.
@ -328,10 +305,11 @@ class AzureBatchJobInfo(object):
def set_job(self, value):
"""Set the label for the job ID, and format the portal URL reference
for the job with this ID.
Except that with the current portal we have no way to directly link to a job.
:param str value: The job ID.
"""
maya.text_field(self._job, edit=True, text=value)
self.url = "https://ms.portal.azure.com"
self.url = "https://portal.azure.com"
def set_pool(self, value):
"""Set the label for the pool ID that this job ran on.
@ -411,6 +389,7 @@ class AzureBatchJobInfo(object):
(self.cancel_button, 'right', 5, 80),
(self.delete_button, 'left', 5, 80)])
self.base.job_selected(self)
maya.execute(self.base.load_tasks)
maya.execute(self.base.get_thumbnail)
maya.refresh()
@ -439,6 +418,7 @@ class AzureBatchJobInfo(object):
def refresh(self):
"""Refresh the details of the specified job, and update the UI."""
self.base.update_job(self.index)
maya.execute(self.base.load_tasks)
maya.execute(self.base.get_thumbnail)
self.selected_dir = utils.get_default_output_path()
maya.text_field(self._dir, edit=True, text=self.selected_dir)

Просмотреть файл

@ -1,30 +1,7 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import os
@ -170,17 +147,19 @@ class AzureBatchPoolInfo(object):
"""
maya.text(self._type, edit=True, label=" {0}".format(value))
def set_size(self, value):
"""Set the number of instances in the pool.
def set_dedicated_size(self, pool):
"""Set the number of instances in the pool, both current and target.
:param int value: Size of the pool.
"""
maya.text(self._size, edit=True, label=" {0}".format(value))
maya.text(self._dedicated_size, edit=True, label=" target: {} current: {}".format(
pool.target_dedicated_nodes, pool.current_dedicated_nodes))
def set_target(self, value):
"""Set the target number of instances in the pool.
:param int value: The target size of the pool.
def set_low_pri_size(self, pool):
"""Set the number of instances in the pool, both current and target.
:param int value: Size of the pool.
"""
maya.text(self._target, edit=True, label=" {0}".format(value))
maya.text(self._low_pri_size, edit=True, label=" target: {} current: {}".format(
pool.target_low_priority_nodes, pool.current_low_priority_nodes))
def set_created(self, value):
"""Set the date/time the pool was created.
@ -205,11 +184,11 @@ class AzureBatchPoolInfo(object):
value += "{} nodes {} ".format(node_states[state], state.value)
maya.text(self._state, edit=True, label=" {0}".format(value))
def set_tasks(self, value):
"""Set the tasks per TVM allowed in the pool.
:param int value: Tasks per TVM.
def set_id(self, value):
"""Set the pool ID field.
:param str value: Pool ID.
"""
maya.text(self._tasks, edit=True, label=" {0}".format(value))
maya.text_field(self._id, edit=True, text=value)
def set_allocation(self, value):
"""Set the allocation state of the pool.
@ -240,12 +219,12 @@ class AzureBatchPoolInfo(object):
"""Command for the expanding of the pool reference frame layout.
Loads latest details for the specified pool and populates UI.
"""
self._id = self.display_data("ID: ")
self._type = self.display_info("Type: ")
self._size = self.display_info("Current Size: ")
self._target = self.display_info("Target Size: ")
self._dedicated_size = self.display_info("Dedicated VMs: ")
self._low_pri_size = self.display_info("Low Priority VMs: ")
self._created = self.display_info("Created: ")
self._state = self.display_info("State: ")
self._tasks = self.display_info("Tasks per VM: ")
self._image = self.display_info("Image: ")
self._allocation = self.display_info("Allocation State: ")
self._licenses = self.display_info("Licenses: ")
@ -253,21 +232,40 @@ class AzureBatchPoolInfo(object):
self.base.pool_selected(self)
auto = self.base.is_auto_pool()
if not auto:
self.content.append(maya.col_layout(
numberOfColumns=5,
columnWidth=((1, 80), (2, 100), (3, 45), (4, 80), (5, 45)),
rowSpacing=(1, 10),
parent=self.layout))
self.resize_button = utils.ProcButton(
"Resize Pool", "Resizing...", self.resize_pool,
parent=self.listbox, align="center")
self.resize_int = maya.int_slider(
value=self.base.get_pool_size(),
"Resize Pool",
"Resizing...",
self.resize_pool,
parent=self.content[-1],
align="center")
self.dedicated_label = maya.text(
label="Dedicated VMs",
parent=self.content[-1])
self.resize_dedicated = maya.int_field(
value=self.base.get_pool_size()[0],
minValue=0,
maxValue=1000,
fieldMinValue=0,
fieldMaxValue=100,
field=True,
width=230,
parent=self.listbox,
annotation="Number of instances to work in pool.")
parent=self.content[-1],
annotation="Number of dedicated VMs in pool.")
self.low_pri_label = maya.text(
label="Low-pri VMs",
parent=self.content[-1])
self.resize_low_pri = maya.int_field(
value=self.base.get_pool_size()[1],
minValue=0,
maxValue=1000,
parent=self.content[-1],
annotation="Number of Low-priority VMs in pool.")
self.content.append(self.resize_button.display)
self.content.append(self.resize_int)
self.content.append(self.dedicated_label)
self.content.append(self.resize_dedicated)
self.content.append(self.low_pri_label)
self.content.append(self.resize_low_pri)
self.delete_button = utils.ProcButton("Delete Pool", "Deleting...",
self.delete_pool, parent=self.layout, align="center")
self.content.append(self.delete_button.display)
@ -305,6 +303,16 @@ class AzureBatchPoolInfo(object):
self.content.append(input)
return input
def display_data(self, label):
"""Display text data as a non-editable text field with heading.
:param str label: The text for the data heading.
"""
self.content.append(
maya.text(label=label, parent=self.listbox, align="right"))
input = maya.text_field(text="", parent=self.listbox, editable=False)
self.content.append(input)
return input
def delete_pool(self, *args):
"""Delete the specified pool."""
self.delete_button.start()
@ -314,7 +322,8 @@ class AzureBatchPoolInfo(object):
def resize_pool(self, *args):
"""Resize the specified pool."""
self.resize_button.start()
resize = maya.int_slider(self.resize_int, query=True, value=True)
self.base.resize_pool(resize)
resize_dedicated = maya.int_field(self.resize_dedicated, query=True, value=True)
resize_low_pri = maya.int_field(self.resize_low_pri, query=True, value=True)
self.base.resize_pool(resize_dedicated, resize_low_pri)
self.base.update_pool(self.index)
self.resize_button.finish()

Просмотреть файл

@ -1,30 +1,7 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import os

Просмотреть файл

@ -1,30 +1,7 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import os
import utils
@ -35,6 +12,11 @@ from api import MayaAPI as maya
class SubmissionUI(object):
"""Class to create the 'Submit' tab in the plug-in UI"""
AUTO_POOL = 1
EXISTING_POOL = 2
NEW_POOL = 3
def __init__(self, base, frame):
"""Create 'Submit' tab and add to UI frame.
@ -46,38 +28,60 @@ class SubmissionUI(object):
self.base = base
self.label = "Submit"
self.page = maya.form_layout(enableBackground=True)
self.select_pool_type = 1
self.select_instances = 1
self.select_pool_type = self.AUTO_POOL
self.select_dedicated_instances = 1
self.select_low_pri_instances = 0
with utils.ScrollLayout(height=475, parent=self.page) as scroll:
box_label = "Pool Settings"
with utils.FrameLayout(label=box_label, collapsable=True):
self.pool_settings = maya.col_layout(
with utils.FrameLayout(label=box_label, collapsable=True) as pool_settings:
self.pool_settings = pool_settings
maya.col_layout(
numberOfColumns=2,
columnWidth=((1, 100), (2, 200)),
rowSpacing=(1, 10),
rowOffset=((1, "top", 20), (2, "bottom", 20)))
rowOffset=((1, "top", 20),))
maya.text(label="Pools: ", align="right")
maya.radio_group(
labelArray3=("Auto provision a pool for this job",
"Reuse an existing persistent pool",
"Create a new persistent pool"),
"Reuse an existing persistent pool",
"Create a new persistent pool"),
numberOfRadioButtons=3,
select=self.select_pool_type,
vertical=True,
onCommand1=self.set_pool_auto,
onCommand2=self.set_pool_reuse,
onCommand3=self.set_pool_new)
self.pool_text = maya.text(
label="Instances: ", align="right")
self.control = maya.int_slider(
field=True, value=self.select_instances,
maya.parent()
self.pool_config = []
self.pool_config.append(maya.col_layout(
numberOfColumns=4,
columnWidth=((1, 100), (2, 50), (3, 100), (4, 50)),
rowSpacing=(1, 10),
rowOffset=((1, "bottom", 20),),
parent=self.pool_settings))
self.pool_config.append(maya.text(
label="Dedicated VMs: ",
align="right",
parent=self.pool_config[0]))
self.pool_config.append(maya.int_field(
value=self.select_dedicated_instances,
minValue=1,
maxValue=1000,
fieldMinValue=1,
fieldMaxValue=1000,
changeCommand=self.set_pool_instances,
annotation="Number of instances in pool")
maxValue=self.base.max_pool_size,
changeCommand=self.set_dedicated_instances,
annotation="Number of dedicated VMs in pool",
parent=self.pool_config[0]))
self.pool_config.append(maya.text(
label="Low-pri VMs: ",
align="right",
parent=self.pool_config[0]))
self.pool_config.append(maya.int_field(
value=self.select_low_pri_instances,
minValue=0,
maxValue=self.base.max_pool_size,
changeCommand=self.set_low_pri_instances,
annotation="Number of low-priority VMs in pool",
parent=self.pool_config[0]))
maya.parent()
box_label = "Render Settings"
@ -207,55 +211,97 @@ class SubmissionUI(object):
:returns: A dictionary with selected pool type as key and pool
specification as value.
"""
if self.select_pool_type == 2:
details = str(maya.menu(self.control, query=True, value=True))
if self.select_pool_type == self.EXISTING_POOL:
details = str(maya.menu(self.pool_config[-1], query=True, value=True))
else:
details = self.select_instances
details = (self.select_dedicated_instances, self.select_low_pri_instances)
return {self.select_pool_type: details}
def set_pool_instances(self, instances):
def set_dedicated_instances(self, instances):
"""Update the number of requested instances in a pool
based on the instance slider.
"""
self.select_instances = instances
self.select_dedicated_instances = instances
def set_low_pri_instances(self, instances):
"""Update the number of requested instances in a pool
based on the instance slider.
"""
self.select_low_pri_instances = instances
def set_pool_new(self, *args):
"""Set selected pool type to be new pool of given size.
Displays the pool size UI control.
Command for select_pool_type radio buttons.
"""
self.select_pool_type = 3
maya.delete_ui(self.control)
maya.text(self.pool_text, edit=True, label="Instances: ")
self.control = maya.int_slider(
field=True,
value=self.select_instances,
self.select_pool_type = self.NEW_POOL
maya.delete_ui(self.pool_config)
self.pool_config = []
self.pool_config.append(maya.col_layout(
numberOfColumns=4,
columnWidth=((1, 100), (2, 50), (3, 100), (4, 50)),
rowSpacing=(1, 10),
rowOffset=((1, "bottom", 20),),
parent=self.pool_settings))
self.pool_config.append(maya.text(
label="Dedicated VMs: ",
align="right",
parent=self.pool_config[0]))
self.pool_config.append(maya.int_field(
value=self.select_dedicated_instances,
minValue=1,
maxValue=1000,
fieldMinValue=1,
fieldMaxValue=1000,
parent=self.pool_settings,
changeCommand=self.set_pool_instances,
annotation="Number of instances in pool")
maxValue=self.base.max_pool_size,
changeCommand=self.set_dedicated_instances,
annotation="Number of dedicated VMs in pool",
parent=self.pool_config[0]))
self.pool_config.append(maya.text(
label="Low-pri VMs: ",
align="right",
parent=self.pool_config[0]))
self.pool_config.append(maya.int_field(
value=self.select_low_pri_instances,
minValue=0,
maxValue=self.base.max_pool_size,
changeCommand=self.set_low_pri_instances,
annotation="Number of low-priority VMs in pool",
parent=self.pool_config[0]))
def set_pool_auto(self, *args):
"""Set selected pool type to be new pool of given size.
Displays the pool size UI control.
Command for select_pool_type radio buttons.
"""
self.select_pool_type = 1
maya.delete_ui(self.control)
maya.text(self.pool_text, edit=True, label="Instances: ")
self.control = maya.int_slider(
field=True,
value=self.select_instances,
self.select_pool_type = self.AUTO_POOL
maya.delete_ui(self.pool_config)
self.pool_config = []
self.pool_config.append(maya.col_layout(
numberOfColumns=4,
columnWidth=((1, 100), (2, 50), (3, 100), (4, 50)),
rowSpacing=(1, 10),
rowOffset=((1, "bottom", 20),),
parent=self.pool_settings))
self.pool_config.append(maya.text(
label="Dedicated VMs: ",
align="right",
parent=self.pool_config[0]))
self.pool_config.append(maya.int_field(
value=self.select_dedicated_instances,
minValue=1,
maxValue=1000,
fieldMinValue=1,
fieldMaxValue=1000,
parent=self.pool_settings,
changeCommand=self.set_pool_instances,
annotation="Number of instances in pool")
maxValue=self.base.max_pool_size,
changeCommand=self.set_dedicated_instances,
annotation="Number of dedicated VMs in pool",
parent=self.pool_config[0]))
self.pool_config.append(maya.text(
label="Low-pri VMs: ",
align="right",
parent=self.pool_config[0]))
self.pool_config.append(maya.int_field(
value=self.select_low_pri_instances,
minValue=0,
maxValue=self.base.max_pool_size,
changeCommand=self.set_low_pri_instances,
annotation="Number of low-priority VMs in pool",
parent=self.pool_config[0]))
def set_pool_reuse(self, *args):
"""Set selected pool type to be an existing pool with given ID.
@ -263,19 +309,25 @@ class SubmissionUI(object):
in a dropdown menu.
Command for select_pool_type radio buttons.
"""
self.select_pool_type = 2
maya.delete_ui(self.control)
maya.text(self.pool_text, edit=True, label="loading...")
self.select_pool_type = self.EXISTING_POOL
maya.delete_ui(self.pool_config)
self.pool_config = []
self.pool_config.append(maya.col_layout(
numberOfColumns=2,
columnWidth=((1, 100), (2, 200)),
rowSpacing=(1, 10),
rowOffset=((1, "bottom", 20),),
parent=self.pool_settings))
self.pool_config.append(maya.text(
label="loading...",
align="right",
parent=self.pool_config[0]))
maya.refresh()
pool_options = self.base.available_pools()
maya.text(self.pool_text, edit=True, label="Pool ID: ")
self.control = maya.menu(
parent=self.pool_settings,
annotation="Use an existing persistent pool ID")
maya.text(self.pool_config[-1], edit=True, label="Pool ID: ")
self.pool_config.append(maya.menu(
annotation="Use an existing persistent pool ID",
parent=self.pool_config[0]))
for pool_id in pool_options:
maya.menu_option(pool_id)
#with utils.Dropdown(None, parent=self.pool_settings, annotation="Use an existing persistent pool ID") as pools:
# self.control = pools
# for pool_id in pool_options:
# self.control.add_item(pool_id)

Просмотреть файл

@ -1,37 +1,16 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
from api import MayaAPI as maya
from enum import Enum
import os
import logging
import platform
import pathlib
from api import MayaAPI as maya
from batch_extensions import _file_utils as file_utils
from exception import CancellationException, FileUploadException
@ -67,7 +46,7 @@ def get_remote_file_path(assetpath):
"""
def generate_path(os_flavor, fullpath=assetpath):
local_sep = os.sep
remote_sep = '\\' if os_flavor == 'Windows' else '/'
remote_sep = '\\' if os_flavor == OperatingSystem.windows else '/'
path = shorten_path(*os.path.split(fullpath))
if ':' in path:
drive_letter, path = path.split(':', 1)
@ -82,7 +61,7 @@ def get_remote_directory(dir_path, os_flavor):
path according to the remote OS.
"""
local_sep = os.sep
remote_sep = '\\' if os_flavor == 'Windows' else '/'
remote_sep = '\\' if os_flavor == OperatingSystem.windows else '/'
if ':' in dir_path:
drive_letter, dir_path = dir_path.split(':', 1)
dir_path = drive_letter + local_sep + dir_path[1:]
@ -95,7 +74,7 @@ def format_scene_path(scene_file, os_flavor):
be on the render node.
"""
scene_path = get_remote_file_path(scene_file)(os_flavor)
if os_flavor == 'Windows':
if os_flavor == OperatingSystem.windows:
return "X:\\\\" + scene_path + '\\\\' + os.path.basename(scene_file)
else:
return "/X/" + scene_path + '/' + os.path.basename(scene_file)
@ -119,6 +98,12 @@ def get_os():
return platform.system()
class OperatingSystem(Enum):
windows = 'Windows'
linux = 'Linux'
darwin = 'Darwin'
class Row(object):
"""UI row class."""
@ -177,7 +162,6 @@ class Layout(object):
return self.layout
def __exit__(self, type, value, traceback):
#TODO: Exception handling should go in here
maya.parent()
@ -447,12 +431,12 @@ class JobWatcher(object):
self.job_watcher = os.path.join(
os.path.dirname(__file__), "tools", "job_watcher.py")
platform = get_os()
if platform == "Windows":
if platform == OperatingSystem.windows.value:
self.proc_cmd = 'system("WMIC PROCESS where (Name=\'mayapy.exe\') get Commandline")'
self.start_cmd = 'system("start mayapy {0}")'
self.quotes = '\\"'
self.splitter = 'mayapy'
elif platform == "Darwin":
elif platform == OperatingSystem.darwin.value:
self.proc_cmd = 'system("ps -ef")'
self.start_cmd = 'system("osascript -e \'tell application \\"Terminal\\" to do script \\"python {0}\\"\'")'
self.quotes = '\\\\\\"'

Просмотреть файл

@ -1,6 +1,6 @@
{
"templateMetadata": {
"description": "Sample application template for working with Blender."
"description": "Application template for working with Maya and Arnold on CentOS."
},
"parameters": {
"sceneFile": {
@ -11,17 +11,12 @@
},
"renderer": {
"type": "string",
"defaultValue": "file",
"defaultValue": "arnold",
"metadata": {
"description": "The Maya renderer to be used for the render"
},
"allowedValues": [
"arnold",
"default",
"sw",
"turtlebake",
"turtle",
"vr"
"arnold"
]
},
"projectData": {
@ -71,6 +66,13 @@
"metadata": {
"description": "The file group where outputs will be stored"
}
},
"logLevel": {
"type": "int",
"defaultValue": 1,
"metadata": {
"description": "Arnold logging verbosity"
}
}
},
"jobPreparationTask": {
@ -113,7 +115,7 @@
"elevationLevel": "admin"
}
},
"commandLine": "sudo mkdir -m a=rwx -p \"/X\";sudo mount --rbind $AZ_BATCH_JOB_PREP_WORKING_DIR/assets /X;Render -renderer [parameters('renderer')] -proj \"$AZ_BATCH_JOB_PREP_WORKING_DIR\" -verb -preRender renderPrep -rd \"$AZ_BATCH_TASK_WORKING_DIR/images\" -s {0} -e {0} \"[parameters('sceneFile')]\";err=$?;python /mnt/resource/batch/tasks/workitems/[parameters('outputs')]/job-1/jobpreparation/wd/thumbnail.py $err;sudo umount \"/X\";exit $err",
"commandLine": "sudo mkdir -m a=rwx -p \"/X\";sudo mount --rbind $AZ_BATCH_JOB_PREP_WORKING_DIR/assets /X;Render -renderer [parameters('renderer')] -proj \"$AZ_BATCH_JOB_PREP_WORKING_DIR\" -ai:ltc 1 -ai:lve [parameters('logLevel')] -verb -preRender renderPrep -rd \"$AZ_BATCH_TASK_WORKING_DIR/images\" -s {0} -e {0} \"[parameters('sceneFile')]\";err=$?;python /mnt/resource/batch/tasks/workitems/[parameters('outputs')]/job-1/jobpreparation/wd/thumbnail.py $err;sudo umount \"/X\";exit $err",
"environmentSettings": [
{
"name": "MAYA_SCRIPT_PATH",
@ -122,10 +124,6 @@
{
"name": "FLEXLM_TIMEOUT",
"value": "5000000"
},
{
"name": "MAYA_RENDER_DESC_PATH",
"value": "/opt/solidangle/mtoa/2017/"
}
],
"outputFiles": [

Просмотреть файл

@ -1,6 +1,6 @@
{
"templateMetadata": {
"description": "Sample application template for working with Blender."
"description": "Application template for working with Maya and Arnold on Windows."
},
"parameters": {
"sceneFile": {
@ -11,17 +11,12 @@
},
"renderer": {
"type": "string",
"defaultValue": "file",
"defaultValue": "arnold",
"metadata": {
"description": "The Maya renderer to be used for the render"
},
"allowedValues": [
"arnold",
"default",
"sw",
"turtlebake",
"turtle",
"vr"
"arnold"
]
},
"projectData": {
@ -71,6 +66,13 @@
"metadata": {
"description": "The file group where outputs will be stored"
}
},
"logLevel": {
"type": "int",
"defaultValue": 1,
"metadata": {
"description": "Arnold logging verbosity"
}
}
},
"jobPreparationTask": {
@ -107,7 +109,7 @@
],
"repeatTask": {
"displayName": "Frame {0}",
"commandLine": "subst X: %AZ_BATCH_JOB_PREP_WORKING_DIR%\\assets & render -renderer [parameters('renderer')] -proj \"%AZ_BATCH_JOB_PREP_WORKING_DIR%\" -verb -preRender renderPrep -rd \"%AZ_BATCH_TASK_WORKING_DIR%\\images\" -s {0} -e {0} \"[parameters('sceneFile')]\" & call mayapy %AZ_BATCH_JOB_PREP_WORKING_DIR%\\thumbnail.py %^errorlevel%",
"commandLine": "subst X: %AZ_BATCH_JOB_PREP_WORKING_DIR%\\assets & render -renderer [parameters('renderer')] -proj \"%AZ_BATCH_JOB_PREP_WORKING_DIR%\" -ai:ltc 1 -ai:lve [parameters('logLevel')] -verb -preRender renderPrep -rd \"%AZ_BATCH_TASK_WORKING_DIR%\\images\" -s {0} -e {0} \"[parameters('sceneFile')]\" & call mayapy %AZ_BATCH_JOB_PREP_WORKING_DIR%\\thumbnail.py %^errorlevel%",
"environmentSettings": [
{
"name": "MAYA_SCRIPT_PATH",

Просмотреть файл

@ -0,0 +1,174 @@
{
"templateMetadata": {
"description": "Application template for working with Maya on CentOS."
},
"parameters": {
"sceneFile": {
"type": "string",
"metadata": {
"description": "The Maya scene file to be rendered"
}
},
"renderer": {
"type": "string",
"defaultValue": "sw",
"metadata": {
"description": "The Maya renderer to be used for the render"
},
"allowedValues": [
"sw"
]
},
"projectData": {
"type": "string",
"metadata": {
"description": "The file group where the input data is stored"
}
},
"assetScript": {
"type": "string",
"metadata": {
"description": "The SAS URL to a pre-render asset path redirection script"
}
},
"thumbScript": {
"type": "string",
"metadata": {
"description": "The SAS URL to the thumbnail generation script"
}
},
"frameStart": {
"type": "int",
"metadata": {
"description": "Index of the first frame to render"
}
},
"workspace": {
"type": "string",
"metadata": {
"description": "The SAS URL to the project workspace"
}
},
"frameStep": {
"type": "int",
"metadata": {
"description": "Incremental step in frame sequeunce"
}
},
"frameEnd": {
"type": "int",
"metadata": {
"description": "Index of the last frame to render"
}
},
"outputs": {
"type": "string",
"metadata": {
"description": "The file group where outputs will be stored"
}
}
},
"jobPreparationTask": {
"resourceFiles": [
{
"source": {
"fileGroup": "[parameters('projectData')]"
},
"filePath": "assets/"
},
{
"blobSource": "[parameters('assetScript')]",
"filePath": "scripts/renderPrep.mel"
},
{
"blobSource": "[parameters('thumbScript')]",
"filePath": "thumbnail.py"
},
{
"blobSource": "[parameters('workspace')]",
"filePath": "workspace.mel"
}
],
"commandLine": "dir"
},
"taskFactory": {
"type": "parametricSweep",
"parameterSets": [
{
"start": "[parameters('frameStart')]",
"end": "[parameters('frameEnd')]",
"step": "[parameters('frameStep')]"
}
],
"repeatTask": {
"displayName": "Frame {0}",
"userIdentity": {
"autoUser": {
"scope": "task",
"elevationLevel": "admin"
}
},
"commandLine": "sudo mkdir -m a=rwx -p \"/X\";sudo mount --rbind $AZ_BATCH_JOB_PREP_WORKING_DIR/assets /X;Render -renderer [parameters('renderer')] -proj \"$AZ_BATCH_JOB_PREP_WORKING_DIR\" -verb -preRender renderPrep -rd \"$AZ_BATCH_TASK_WORKING_DIR/images\" -s {0} -e {0} \"[parameters('sceneFile')]\";err=$?;python /mnt/resource/batch/tasks/workitems/[parameters('outputs')]/job-1/jobpreparation/wd/thumbnail.py $err;sudo umount \"/X\";exit $err",
"environmentSettings": [
{
"name": "MAYA_SCRIPT_PATH",
"value": "/mnt/resource/batch/tasks/workitems/[parameters('outputs')]/job-1/jobpreparation/wd/scripts"
},
{
"name": "FLEXLM_TIMEOUT",
"value": "5000000"
}
],
"outputFiles": [
{
"filePattern": "images/**/*",
"destination": {
"autoStorage": {
"fileGroup": "[parameters('outputs')]"
}
},
"uploadOptions": {
"uploadCondition": "taskSuccess"
}
},
{
"filePattern": "thumbs/*.png",
"destination": {
"autoStorage": {
"fileGroup": "[parameters('outputs')]",
"path": "thumbs"
}
},
"uploadOptions": {
"uploadCondition": "taskSuccess"
}
},
{
"filePattern": "../stdout.txt",
"destination": {
"autoStorage": {
"fileGroup": "[parameters('outputs')]",
"path": "logs/frame_{0}.log"
}
},
"uploadOptions": {
"uploadCondition": "taskCompletion"
}
},
{
"filePattern": "../stderr.txt",
"destination": {
"autoStorage": {
"fileGroup": "[parameters('outputs')]",
"path": "logs/frame_{0}_error.log"
}
},
"uploadOptions": {
"uploadCondition": "taskCompletion"
}
}
]
}
},
"onAllTasksComplete": "terminateJob"
}

Просмотреть файл

@ -0,0 +1,168 @@
{
"templateMetadata": {
"description": "Application template for working with Maya on Windows."
},
"parameters": {
"sceneFile": {
"type": "string",
"metadata": {
"description": "The Maya scene file to be rendered"
}
},
"renderer": {
"type": "string",
"defaultValue": "sw",
"metadata": {
"description": "The Maya renderer to be used for the render"
},
"allowedValues": [
"sw"
]
},
"projectData": {
"type": "string",
"metadata": {
"description": "The file group where the input data is stored"
}
},
"assetScript": {
"type": "string",
"metadata": {
"description": "The SAS URL to a pre-render asset path redirection script"
}
},
"thumbScript": {
"type": "string",
"metadata": {
"description": "The SAS URL to the thumbnail generation script"
}
},
"workspace": {
"type": "string",
"metadata": {
"description": "The SAS URL to the project workspace"
}
},
"frameStart": {
"type": "int",
"metadata": {
"description": "Index of the first frame to render"
}
},
"frameStep": {
"type": "int",
"metadata": {
"description": "Incremental step in frame sequeunce"
}
},
"frameEnd": {
"type": "int",
"metadata": {
"description": "Index of the last frame to render"
}
},
"outputs": {
"type": "string",
"metadata": {
"description": "The file group where outputs will be stored"
}
}
},
"jobPreparationTask": {
"resourceFiles": [
{
"source": {
"fileGroup": "[parameters('projectData')]"
},
"filePath": "assets\\"
},
{
"blobSource": "[parameters('assetScript')]",
"filePath": "scripts\\renderPrep.mel"
},
{
"blobSource": "[parameters('thumbScript')]",
"filePath": "thumbnail.py"
},
{
"blobSource": "[parameters('workspace')]",
"filePath": "workspace.mel"
}
],
"commandLine": "dir"
},
"taskFactory": {
"type": "parametricSweep",
"parameterSets": [
{
"start": "[parameters('frameStart')]",
"end": "[parameters('frameEnd')]",
"step": "[parameters('frameStep')]"
}
],
"repeatTask": {
"displayName": "Frame {0}",
"commandLine": "subst X: %AZ_BATCH_JOB_PREP_WORKING_DIR%\\assets & render -renderer [parameters('renderer')] -proj \"%AZ_BATCH_JOB_PREP_WORKING_DIR%\" -verb -preRender renderPrep -rd \"%AZ_BATCH_TASK_WORKING_DIR%\\images\" -s {0} -e {0} \"[parameters('sceneFile')]\" & call mayapy %AZ_BATCH_JOB_PREP_WORKING_DIR%\\thumbnail.py %^errorlevel%",
"environmentSettings": [
{
"name": "MAYA_SCRIPT_PATH",
"value": "%AZ_BATCH_JOB_PREP_WORKING_DIR%\\scripts"
},
{
"name": "FLEXLM_TIMEOUT",
"value": "5000000"
}
],
"outputFiles": [
{
"filePattern": "images/**/*",
"destination": {
"autoStorage": {
"fileGroup": "[parameters('outputs')]"
}
},
"uploadOptions": {
"uploadCondition": "taskSuccess"
}
},
{
"filePattern": "thumbs/*.png",
"destination": {
"autoStorage": {
"fileGroup": "[parameters('outputs')]",
"path": "thumbs"
}
},
"uploadOptions": {
"uploadCondition": "taskSuccess"
}
},
{
"filePattern": "../stdout.txt",
"destination": {
"autoStorage": {
"fileGroup": "[parameters('outputs')]",
"path": "logs/frame_{0}.log"
}
},
"uploadOptions": {
"uploadCondition": "taskCompletion"
}
},
{
"filePattern": "../stderr.txt",
"destination": {
"autoStorage": {
"fileGroup": "[parameters('outputs')]",
"path": "logs/frame_{0}_error.log"
}
},
"uploadOptions": {
"uploadCondition": "taskCompletion"
}
}
]
}
},
"onAllTasksComplete": "terminateJob"
}

Просмотреть файл

@ -76,7 +76,7 @@ You can also delete the pool and resize the number of VMs in the pool. A pool ca
## Monitoring jobs
Once you have submitted a job, you can monitor it both via the [Azure Management Portal](http://ms.portal.azure.com/) and the `Jobs` tab of the plug-in.
Once you have submitted a job, you can monitor it both via the [Azure Management Portal](http://portal.azure.com/) and the `Jobs` tab of the plug-in.
Selecting a listed job will display the current state of the job. You can also use this tab to cancel and delete jobs, as well as download the outputs and rendering logs.
To download outputs, use the `Outputs` field to set the desired destination directory, and click the center button (with the gear icon) to start a background process that will
watch the job and download outputs as it progresses. You can close Maya without disrupting the download.

Просмотреть файл

@ -32,7 +32,7 @@ import subprocess
import shutil
import zipfile
VERSION = "0.9.0"
VERSION = "0.10.0"
def main():
"""Build Maya Plug-in package"""

Просмотреть файл

@ -1,79 +0,0 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
import sys
import os
if sys.version_info[:2] < (2, 7, ):
try:
import unittest2
from unittest2 import TestLoader, TextTestRunner
except ImportError:
print("The Batch Maya Plugin test suite requires "
"the unittest2 package to run on Python 2.6 and "
"below.\nPlease install this package to continue.")
sys.exit()
else:
import unittest
from unittest import TestLoader, TextTestRunner
if sys.version_info[:2] >= (3, 3, ):
from unittest import mock
else:
try:
import mock
except ImportError:
print("The Batch Maya Plugin test suite requires "
"the mock package to run on Python 3.2 and below.\n"
"Please install this package to continue.")
raise
if __name__ == '__main__':
runner = TextTestRunner(verbosity=2)
test_dir = os.path.dirname(__file__)
top_dir = os.path.dirname(test_dir)
src_dir = os.path.join(top_dir, 'azure_batch_maya', 'scripts')
mod_dir = os.path.join(test_dir, 'data', 'modules')
ui_dir = os.path.join(src_dir, 'ui')
tools_dir = os.path.join(src_dir, 'tools')
os.environ["AZUREBATCH_ICONS"] = os.path.join(top_dir, 'azure_batch_maya', 'icons')
os.environ["AZUREBATCH_MODULES"] = mod_dir
os.environ["AZUREBATCH_SCRIPTS"] = "{0};{1};{2}".format(src_dir, ui_dir, tools_dir)
sys.path.extend([src_dir, ui_dir, tools_dir, mod_dir])
test_loader = TestLoader()
suite = test_loader.discover(test_dir,
pattern="test_*.py",
top_level_dir=top_dir)
runner.run(suite)

Просмотреть файл

@ -1,31 +1,7 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import sys
import os
@ -49,6 +25,7 @@ from assets import Asset, Assets, AzureBatchAssets
from exception import FileUploadException
from utils import ProgressBar, ProcButton
class TestAsset(unittest.TestCase):
def setUp(self):
@ -438,6 +415,7 @@ class TestAzureBatchAssets(unittest.TestCase):
os.environ["AZUREBATCH_TEMPLATES"] = os.path.join(top_dir, 'azure_batch_maya', 'templates')
os.environ["AZUREBATCH_MODULES"] = mod_dir
os.environ["AZUREBATCH_SCRIPTS"] = "{0};{1};{2}".format(src_dir, ui_dir, tools_dir)
os.environ["AZUREBATCH_VERSION"] = "0.1"
return super(TestAzureBatchAssets, self).setUp()
@ -445,7 +423,7 @@ class TestAzureBatchAssets(unittest.TestCase):
@mock.patch("assets.callback")
@mock.patch("assets.AssetsUI")
def test_batchassets_create(self, mock_ui, mock_call, mock_collect):
assets = AzureBatchAssets("frame", "call")
assets = AzureBatchAssets(3, "frame", "call")
mock_ui.assert_called_with(assets, "frame")
mock_collect.assert_called_with()
#mock_call.after_new.assert_called_with(assets.callback_refresh)

Просмотреть файл

@ -20,7 +20,6 @@ from batch_extensions import _pool_utils as pool_utils
from batch_extensions import _file_utils as file_utils
class TestBatchExtensions(unittest.TestCase):
# pylint: disable=attribute-defined-outside-init,no-member,too-many-public-methods

Просмотреть файл

@ -1,3 +1,8 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
try:
import unittest2 as unittest
except ImportError:

Просмотреть файл

@ -1,31 +1,7 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import sys
import os
@ -61,7 +37,7 @@ class AzureTestBatchPools(unittest.TestCase):
@mock.patch("pools.PoolsUI")
def test_pools_initialize(self, mock_ui):
pools = AzureBatchPools("frame", "call")
pools = AzureBatchPools(4, "frame", "call")
mock_ui.assert_called_with(pools, "frame")
def test_pools_configure(self):
@ -74,9 +50,15 @@ class AzureTestBatchPools(unittest.TestCase):
self.mock_self.batch.pool = mock.create_autospec(batch.operations.ExtendedPoolOperations)
pool1 = mock.create_autospec(models.CloudPool)
pool1.id = "12345"
pool1.virtual_machine_configuration = mock.create_autospec(models.VirtualMachineConfiguration)
pool1.virtual_machine_configuration.image_reference = mock.create_autospec(models.ImageReference)
pool1.virtual_machine_configuration.image_reference.publisher = "MicrosoftWindows"
pool1.creation_time = datetime.datetime.now()
pool2 = mock.create_autospec(models.CloudPool)
pool2.id = "67890"
pool2.virtual_machine_configuration = mock.create_autospec(models.VirtualMachineConfiguration)
pool2.virtual_machine_configuration.image_reference = mock.create_autospec(models.ImageReference)
pool2.virtual_machine_configuration.image_reference.publisher = "LinuxUbuntu"
pool2.creation_time = datetime.datetime.now()
self.mock_self._call = lambda x: [pool1, pool2]
@ -85,7 +67,9 @@ class AzureTestBatchPools(unittest.TestCase):
self.assertEqual(len(self.mock_self.pools), 0)
pool1.id = "Maya_Pool_A"
pool1.virtual_machine_configuration.image_reference.publisher = "batch"
pool2.id = "Maya_Auto_Pool_B"
pool2.virtual_machine_configuration.image_reference.publisher = "batch"
ids = AzureBatchPools.list_pools(self.mock_self)
self.assertEqual(ids, ["Maya_Pool_A"])
self.assertEqual(len(self.mock_self.pools), 2)

Просмотреть файл

@ -1,30 +1,7 @@
#-------------------------------------------------------------------------
#
# Azure Batch Maya Plugin
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#--------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import sys
import os
@ -48,6 +25,7 @@ from pools import AzureBatchPools
from environment import AzureBatchEnvironment
from shared import AzureBatchSettings
from exception import CancellationException
from utils import OperatingSystem
from batch_extensions import BatchExtensionsClient
from batch_extensions.batch_auth import SharedKeyCredentials
@ -56,11 +34,11 @@ from batch_extensions import models
from azure.storage.blob import BlockBlobService
LIVE = True
def print_status(status):
print(status)
class TestBatchSubmission(unittest.TestCase):
def setUp(self):
@ -74,6 +52,7 @@ class TestBatchSubmission(unittest.TestCase):
os.environ["AZUREBATCH_TEMPLATES"] = os.path.join(top_dir, 'azure_batch_maya', 'templates')
os.environ["AZUREBATCH_MODULES"] = mod_dir
os.environ["AZUREBATCH_SCRIPTS"] = "{0};{1};{2}".format(src_dir, ui_dir, tools_dir)
os.environ["AZUREBATCH_VERSION"] = "0.1"
self.mock_self = mock.create_autospec(AzureBatchSubmission)
self.mock_self.batch = mock.create_autospec(BatchExtensionsClient)
self.mock_self.batch.job = mock.create_autospec(operations.ExtendedJobOperations)
@ -93,7 +72,7 @@ class TestBatchSubmission(unittest.TestCase):
@mock.patch("submission.callback")
@mock.patch("submission.SubmissionUI")
def test_submission_create(self, mock_ui, mock_call, mock_mods):
submission = AzureBatchSubmission("frame", "call")
submission = AzureBatchSubmission(2, "frame", "call")
mock_mods.assert_called_with()
mock_ui.assert_called_with(submission, "frame")
#mock_call.after_new.assert_called_with(mock.ANY)
@ -161,11 +140,11 @@ class TestBatchSubmission(unittest.TestCase):
mock_utils.format_scene_path.return_value = "test_file_path"
self.mock_self._configure_pool = lambda t: AzureBatchSubmission._configure_pool(self.mock_self, t)
self.mock_self._check_plugins.return_value = []
self.mock_self._get_os_flavor.return_value = 'Windows'
self.mock_self._get_os_flavor.return_value = OperatingSystem.windows
self.mock_self.pool_manager.create_auto_pool.return_value = {'autoPool': 'auto-pool'}
self.mock_self.pool_manager.create_pool.return_value = {'poolId': 'new-pool'}
self.mock_self.env_manager.get_environment_settings.return_value = [{'name':'foo', 'value':'bar'}]
self.mock_self.renderer = mock.Mock()
self.mock_self.renderer = mock.Mock(render_engine='arnold')
self.mock_self.renderer.get_jobdata.return_value = ("a", "b")
self.mock_self.renderer.get_params.return_value = {"foo": "bar"}
self.mock_self.renderer.get_title.return_value = "job name"