opencue-cuecmd 1.13.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cuecmd/__init__.py +33 -0
- cuecmd/__main__.py +41 -0
- cuecmd/execute_commands.py +67 -0
- cuecmd/main.py +239 -0
- opencue_cuecmd-1.13.6.dist-info/METADATA +403 -0
- opencue_cuecmd-1.13.6.dist-info/RECORD +8 -0
- opencue_cuecmd-1.13.6.dist-info/WHEEL +4 -0
- opencue_cuecmd-1.13.6.dist-info/entry_points.txt +2 -0
cuecmd/__init__.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Copyright Contributors to the OpenCue Project
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
"""OpenCue Cuecmd command execution tool."""
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
__version__ = version("opencue_cuecmd")
|
|
23
|
+
except PackageNotFoundError:
|
|
24
|
+
# Package is not installed
|
|
25
|
+
__version__ = "1.0.0"
|
|
26
|
+
except ImportError:
|
|
27
|
+
# Python < 3.8
|
|
28
|
+
try:
|
|
29
|
+
import pkg_resources
|
|
30
|
+
|
|
31
|
+
__version__ = pkg_resources.get_distribution("opencue_cuecmd").version
|
|
32
|
+
except Exception:
|
|
33
|
+
__version__ = "1.0.0"
|
cuecmd/__main__.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Copyright Contributors to the OpenCue Project
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
"""Entrypoint for Cuecmd tool."""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
from __future__ import absolute_import, division, print_function
|
|
20
|
+
|
|
21
|
+
import logging
|
|
22
|
+
import sys
|
|
23
|
+
import warnings
|
|
24
|
+
|
|
25
|
+
from cuecmd import main as opencuecmd
|
|
26
|
+
|
|
27
|
+
# Suppress protobuf version warnings
|
|
28
|
+
warnings.filterwarnings(
|
|
29
|
+
"ignore", category=UserWarning, module="google.protobuf.runtime_version"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
logger = logging.getLogger("opencue.tools.cuecmd")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def main():
|
|
36
|
+
"""Main entry point for cuecmd."""
|
|
37
|
+
opencuecmd.main(sys.argv)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
if __name__ == "__main__":
|
|
41
|
+
main()
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# Copyright Contributors to the OpenCue Project
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
"""Helper script to execute commands from a file based on frame number and chunk size."""
|
|
18
|
+
|
|
19
|
+
import argparse
|
|
20
|
+
import subprocess
|
|
21
|
+
import sys
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def main():
|
|
25
|
+
"""Execute commands from a file based on frame number and chunk size."""
|
|
26
|
+
parser = argparse.ArgumentParser(
|
|
27
|
+
description="Execute commands from a file based on frame number and chunk size."
|
|
28
|
+
)
|
|
29
|
+
parser.add_argument("command_file", help="Path to the file containing commands")
|
|
30
|
+
parser.add_argument(
|
|
31
|
+
"chunk_size", type=int, help="Number of commands to execute per frame"
|
|
32
|
+
)
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
"--frame", type=int, required=True, help="Frame number to execute"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
args = parser.parse_args()
|
|
38
|
+
|
|
39
|
+
# Read all commands from the file
|
|
40
|
+
with open(args.command_file, "r", encoding="utf-8") as f:
|
|
41
|
+
commands = [line.strip() for line in f if line.strip()]
|
|
42
|
+
|
|
43
|
+
# Calculate which commands to run for this frame
|
|
44
|
+
start_idx = (args.frame - 1) * args.chunk_size
|
|
45
|
+
end_idx = min(start_idx + args.chunk_size, len(commands))
|
|
46
|
+
|
|
47
|
+
if start_idx >= len(commands):
|
|
48
|
+
print(f"Frame {args.frame} is beyond the available commands")
|
|
49
|
+
return 0
|
|
50
|
+
|
|
51
|
+
# Execute each command in the chunk
|
|
52
|
+
for idx in range(start_idx, end_idx):
|
|
53
|
+
cmd = commands[idx]
|
|
54
|
+
print(f"Executing command {idx + 1}: {cmd}")
|
|
55
|
+
|
|
56
|
+
# Execute the command
|
|
57
|
+
result = subprocess.run(cmd, shell=True, check=False)
|
|
58
|
+
|
|
59
|
+
if result.returncode != 0:
|
|
60
|
+
print(f"Command failed with return code {result.returncode}: {cmd}")
|
|
61
|
+
sys.exit(result.returncode)
|
|
62
|
+
|
|
63
|
+
return 0
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
if __name__ == "__main__":
|
|
67
|
+
main()
|
cuecmd/main.py
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# Copyright Contributors to the OpenCue Project
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
Cuecmd - Execute a list of commands on the OpenCue render farm.
|
|
19
|
+
|
|
20
|
+
This tool reads a file containing a list of commands and submits them as
|
|
21
|
+
frames to be executed on the render farm. Commands can be chunked together
|
|
22
|
+
to run multiple commands per frame.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import argparse
|
|
26
|
+
import math
|
|
27
|
+
import os
|
|
28
|
+
import shutil
|
|
29
|
+
import sys
|
|
30
|
+
import tempfile
|
|
31
|
+
import traceback
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
|
|
34
|
+
import outline
|
|
35
|
+
from outline.modules.shell import Shell
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def parse_arguments(argv):
|
|
39
|
+
"""Parse command-line arguments."""
|
|
40
|
+
parser = argparse.ArgumentParser(
|
|
41
|
+
description="Execute a list of commands on the OpenCue render farm.",
|
|
42
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
43
|
+
epilog="""
|
|
44
|
+
Examples:
|
|
45
|
+
%(prog)s commands.txt
|
|
46
|
+
%(prog)s commands.txt --chunk 5 --cores 2 --memory 4
|
|
47
|
+
%(prog)s commands.txt --pause --pretend
|
|
48
|
+
""",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
parser.add_argument(
|
|
52
|
+
"command_file", help="A file with a list of commands to run (one per line)"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
parser.add_argument(
|
|
56
|
+
"-c",
|
|
57
|
+
"--chunk",
|
|
58
|
+
type=int,
|
|
59
|
+
default=1,
|
|
60
|
+
help="Number of commands to chunk per frame (Default: 1)",
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
parser.add_argument(
|
|
64
|
+
"--cores",
|
|
65
|
+
type=float,
|
|
66
|
+
default=1.0,
|
|
67
|
+
help="Number of cores required per frame (Default: 1.0)",
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
parser.add_argument(
|
|
71
|
+
"--memory",
|
|
72
|
+
type=float,
|
|
73
|
+
default=1.0,
|
|
74
|
+
help="Amount of RAM in GB required per frame (Default: 1.0)",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
parser.add_argument(
|
|
78
|
+
"-p", "--pause", action="store_true", help="Launch the job in the paused state"
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
parser.add_argument(
|
|
82
|
+
"--pretend",
|
|
83
|
+
action="store_true",
|
|
84
|
+
help="Generate the outline and print the launch info without submitting",
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
parser.add_argument(
|
|
88
|
+
"-s",
|
|
89
|
+
"--shot",
|
|
90
|
+
default=os.environ.get("SHOT", "default"),
|
|
91
|
+
help='Shot name for the job (Default: from $SHOT or "default")',
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
parser.add_argument(
|
|
95
|
+
"--show",
|
|
96
|
+
default=os.environ.get("SHOW", "default"),
|
|
97
|
+
help='Show name for the job (Default: from $SHOW or "default")',
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
parser.add_argument(
|
|
101
|
+
"--user",
|
|
102
|
+
default=os.environ.get("USER", "unknown"),
|
|
103
|
+
help='User name for the job (Default: from $USER or "unknown")',
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
parser.add_argument(
|
|
107
|
+
"--job-name", help="Custom job name (Default: auto-generated from file name)"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
return parser.parse_args(argv[1:])
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def count_commands(command_file):
|
|
114
|
+
"""Count the number of non-empty commands in the file."""
|
|
115
|
+
count = 0
|
|
116
|
+
with open(command_file, "r", encoding="utf-8") as f:
|
|
117
|
+
for line in f:
|
|
118
|
+
if line.strip():
|
|
119
|
+
count += 1
|
|
120
|
+
return count
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def get_frame_range(command_count, chunk_size):
|
|
124
|
+
"""Calculate the frame range based on command count and chunk size."""
|
|
125
|
+
num_frames = math.ceil(command_count / chunk_size)
|
|
126
|
+
return f"1-{num_frames}"
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def copy_to_temp(command_file):
|
|
130
|
+
"""Copy the command file to a temporary location that will be accessible during execution."""
|
|
131
|
+
# Create a temporary directory
|
|
132
|
+
temp_dir = tempfile.mkdtemp(prefix="cuecmd_")
|
|
133
|
+
basename = Path(command_file).stem
|
|
134
|
+
temp_file = os.path.join(temp_dir, f"{basename}.cmds")
|
|
135
|
+
shutil.copyfile(command_file, temp_file)
|
|
136
|
+
return temp_file
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def create_outline(args, command_file, frame_range):
|
|
140
|
+
"""Create an outline for executing the commands."""
|
|
141
|
+
# Get the path to the execute_commands.py script
|
|
142
|
+
execute_script = os.path.join(os.path.dirname(__file__), "execute_commands.py")
|
|
143
|
+
|
|
144
|
+
# Create the job name
|
|
145
|
+
if args.job_name:
|
|
146
|
+
job_name = args.job_name
|
|
147
|
+
else:
|
|
148
|
+
basename = Path(args.command_file).stem
|
|
149
|
+
job_name = f"{args.show}_{args.shot}_{args.user}_{basename}"
|
|
150
|
+
|
|
151
|
+
# Create the outline
|
|
152
|
+
ol = outline.Outline(job_name, shot=args.shot, show=args.show, user=args.user)
|
|
153
|
+
|
|
154
|
+
# Create the shell command that will execute the commands
|
|
155
|
+
# Use python3 explicitly
|
|
156
|
+
cmd = f"python3 {execute_script} {command_file} {args.chunk} --frame #IFRAME#"
|
|
157
|
+
|
|
158
|
+
# Create the layer
|
|
159
|
+
layer = Shell(
|
|
160
|
+
"cuecmd",
|
|
161
|
+
command=cmd,
|
|
162
|
+
chunk=1,
|
|
163
|
+
threads=args.cores,
|
|
164
|
+
threadable=1,
|
|
165
|
+
range=frame_range,
|
|
166
|
+
memory=f"{int(args.memory * 1024)}MB",
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
ol.add_layer(layer)
|
|
170
|
+
return ol
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def main(argv=None):
|
|
174
|
+
"""Main entry point for cuecmd."""
|
|
175
|
+
if argv is None:
|
|
176
|
+
argv = sys.argv
|
|
177
|
+
|
|
178
|
+
args = parse_arguments(argv)
|
|
179
|
+
|
|
180
|
+
# Validate the command file exists
|
|
181
|
+
if not os.path.isfile(args.command_file):
|
|
182
|
+
print(f"Error: Command file not found: {args.command_file}", file=sys.stderr)
|
|
183
|
+
sys.exit(1)
|
|
184
|
+
|
|
185
|
+
# Get absolute path to the command file
|
|
186
|
+
command_file = os.path.abspath(args.command_file)
|
|
187
|
+
|
|
188
|
+
# Count commands and calculate range
|
|
189
|
+
command_count = count_commands(command_file)
|
|
190
|
+
if command_count == 0:
|
|
191
|
+
print("Error: No commands found in the command file", file=sys.stderr)
|
|
192
|
+
sys.exit(1)
|
|
193
|
+
|
|
194
|
+
print(f"Found {command_count} commands in {command_file}")
|
|
195
|
+
|
|
196
|
+
frame_range = get_frame_range(command_count, args.chunk)
|
|
197
|
+
print(f"Frame range with chunking of {args.chunk}: {frame_range}")
|
|
198
|
+
|
|
199
|
+
# Copy command file to a temporary location
|
|
200
|
+
temp_command_file = copy_to_temp(command_file)
|
|
201
|
+
print(f"Copied commands to: {temp_command_file}")
|
|
202
|
+
|
|
203
|
+
# Create the outline
|
|
204
|
+
ol = create_outline(args, temp_command_file, frame_range)
|
|
205
|
+
|
|
206
|
+
if args.pretend:
|
|
207
|
+
print("\n=== Pretend Mode ===")
|
|
208
|
+
print(f"Job name: {ol.get_name()}")
|
|
209
|
+
print(f"Shot: {ol.get_shot()}")
|
|
210
|
+
print(f"Show: {ol.get_show()}")
|
|
211
|
+
print(f"User: {ol.get_user()}")
|
|
212
|
+
print(f"Frame range: {frame_range}")
|
|
213
|
+
print(f"Chunk size: {args.chunk}")
|
|
214
|
+
print(f"Cores per frame: {args.cores}")
|
|
215
|
+
print(f"Memory per frame: {args.memory}GB")
|
|
216
|
+
print(f"Command file: {temp_command_file}")
|
|
217
|
+
print("\nWould launch the job with the above settings.")
|
|
218
|
+
return 0
|
|
219
|
+
|
|
220
|
+
# Launch the job
|
|
221
|
+
try:
|
|
222
|
+
jobs = outline.cuerun.launch(ol, pause=args.pause, use_pycuerun=True)
|
|
223
|
+
if jobs:
|
|
224
|
+
print(f"\nSuccessfully launched job: {ol.get_name()}")
|
|
225
|
+
if args.pause:
|
|
226
|
+
print("Job is paused. Use cueadmin to unpause it.")
|
|
227
|
+
else:
|
|
228
|
+
print("Failed to launch job", file=sys.stderr)
|
|
229
|
+
sys.exit(1)
|
|
230
|
+
except Exception as e:
|
|
231
|
+
print(f"Error launching job: {e}", file=sys.stderr)
|
|
232
|
+
traceback.print_exc()
|
|
233
|
+
sys.exit(1)
|
|
234
|
+
|
|
235
|
+
return 0
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
if __name__ == "__main__":
|
|
239
|
+
sys.exit(main())
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: opencue_cuecmd
|
|
3
|
+
Version: 1.13.6
|
|
4
|
+
Summary: Cuecmd is a command-line tool for OpenCue that executes a list of commands as frames on the render farm. It enables batch processing by chunking commands and submitting them as OpenCue jobs.
|
|
5
|
+
Requires-Python: >3.7
|
|
6
|
+
Requires-Dist: opencue-pycue
|
|
7
|
+
Requires-Dist: opencue-pyoutline
|
|
8
|
+
Provides-Extra: dev
|
|
9
|
+
Requires-Dist: black; extra == 'dev'
|
|
10
|
+
Requires-Dist: isort; extra == 'dev'
|
|
11
|
+
Requires-Dist: mock==2.0.0; extra == 'dev'
|
|
12
|
+
Requires-Dist: pyfakefs==5.2.3; extra == 'dev'
|
|
13
|
+
Requires-Dist: pylint; extra == 'dev'
|
|
14
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
15
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
16
|
+
Requires-Dist: pytest-mock; extra == 'dev'
|
|
17
|
+
Provides-Extra: test
|
|
18
|
+
Requires-Dist: mock==2.0.0; extra == 'test'
|
|
19
|
+
Requires-Dist: pyfakefs==5.2.3; extra == 'test'
|
|
20
|
+
Requires-Dist: pytest; extra == 'test'
|
|
21
|
+
Requires-Dist: pytest-cov; extra == 'test'
|
|
22
|
+
Requires-Dist: pytest-mock; extra == 'test'
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# OpenCue Cuecmd
|
|
26
|
+
|
|
27
|
+
Cuecmd is a command-line tool for OpenCue that executes a list of commands as frames on the render farm. It enables batch processing by chunking commands and submitting them as OpenCue jobs.
|
|
28
|
+
|
|
29
|
+
## Table of Contents
|
|
30
|
+
|
|
31
|
+
1. [Overview](#overview)
|
|
32
|
+
2. [Features](#features)
|
|
33
|
+
3. [Installation](#installation)
|
|
34
|
+
4. [Quick Start](#quick-start)
|
|
35
|
+
5. [Usage](#usage)
|
|
36
|
+
6. [Options](#options)
|
|
37
|
+
7. [Examples](#examples)
|
|
38
|
+
8. [Environment Variables](#environment-variables)
|
|
39
|
+
9. [How It Works](#how-it-works)
|
|
40
|
+
10. [Running Tests](#running-tests)
|
|
41
|
+
11. [Docker Support](#docker-support)
|
|
42
|
+
12. [Contributing](#contributing)
|
|
43
|
+
|
|
44
|
+
## Overview
|
|
45
|
+
|
|
46
|
+
`cuecmd` is a tool for executing arbitrary shell commands on the OpenCue render farm. It takes a text file containing a list of commands (one per line) and distributes them across multiple frames for parallel execution.
|
|
47
|
+
|
|
48
|
+
### About Cuecmd
|
|
49
|
+
|
|
50
|
+
The name "cuecmd" combines:
|
|
51
|
+
- **"Cue"** - From OpenCue, the render farm management system
|
|
52
|
+
- **"cmd"** - Short for command, representing its purpose of executing commands
|
|
53
|
+
|
|
54
|
+
The `cuecmd` provides a simple way to parallelize any batch of shell commands using the OpenCue infrastructure.
|
|
55
|
+
|
|
56
|
+
## Features
|
|
57
|
+
|
|
58
|
+
- **Command batching**: Execute any list of shell commands on the render farm
|
|
59
|
+
- **Chunking**: Group multiple commands per frame for efficient resource usage
|
|
60
|
+
- **Resource control**: Specify cores and memory requirements per frame
|
|
61
|
+
- **Job management**: Launch jobs paused or test with pretend mode
|
|
62
|
+
- **Flexible configuration**: Override show, shot, and user from command line
|
|
63
|
+
- **Simple input format**: Plain text file with one command per line
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
### Prerequisites
|
|
68
|
+
- OpenCue server running and accessible
|
|
69
|
+
- Python 3.7 or higher
|
|
70
|
+
- OpenCue Python packages installed
|
|
71
|
+
|
|
72
|
+
### Install Methods
|
|
73
|
+
|
|
74
|
+
Install as part of the OpenCue Python packages:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pip install opencue-cuecmd
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Or install from source:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
cd OpenCue/cuecmd
|
|
84
|
+
pip install .
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Configuration
|
|
88
|
+
Set up your environment variables:
|
|
89
|
+
```bash
|
|
90
|
+
export OPENCUE_HOSTS="your-cuebot-server:8443"
|
|
91
|
+
export OPENCUE_FACILITY="your-facility-code"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Quick Start
|
|
95
|
+
|
|
96
|
+
1. **Create a command file:**
|
|
97
|
+
```bash
|
|
98
|
+
cat > commands.txt << EOF
|
|
99
|
+
echo "Processing file 1"
|
|
100
|
+
echo "Processing file 2"
|
|
101
|
+
echo "Processing file 3"
|
|
102
|
+
EOF
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
2. **Submit to OpenCue:**
|
|
106
|
+
```bash
|
|
107
|
+
cuecmd commands.txt
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
3. **Monitor the job:**
|
|
111
|
+
```bash
|
|
112
|
+
cueadmin -lj # List jobs
|
|
113
|
+
cueman -lf <job_name> # List frames
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Usage
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
cuecmd [options] <command_file>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Basic Usage
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# Submit a simple command list
|
|
126
|
+
cuecmd commands.txt
|
|
127
|
+
|
|
128
|
+
# Chunk 5 commands per frame
|
|
129
|
+
cuecmd commands.txt --chunk 5
|
|
130
|
+
|
|
131
|
+
# Specify resources
|
|
132
|
+
cuecmd commands.txt --cores 4 --memory 8
|
|
133
|
+
|
|
134
|
+
# Launch paused for review
|
|
135
|
+
cuecmd commands.txt --pause
|
|
136
|
+
|
|
137
|
+
# Test without submitting
|
|
138
|
+
cuecmd commands.txt --pretend
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Options
|
|
142
|
+
|
|
143
|
+
### Required Arguments
|
|
144
|
+
- `command_file` - A text file with a list of commands to run (one per line)
|
|
145
|
+
|
|
146
|
+
### Optional Arguments
|
|
147
|
+
|
|
148
|
+
#### Execution Options
|
|
149
|
+
- `-c, --chunk <N>` - Number of commands to chunk per frame (Default: 1)
|
|
150
|
+
- `--cores <N>` - Number of cores required per frame (Default: 1.0)
|
|
151
|
+
- `--memory <N>` - Amount of RAM in GB required per frame (Default: 1.0)
|
|
152
|
+
|
|
153
|
+
#### Job Control
|
|
154
|
+
- `-p, --pause` - Launch the job in the paused state
|
|
155
|
+
- `--pretend` - Generate the outline and print info without submitting
|
|
156
|
+
|
|
157
|
+
#### Job Metadata
|
|
158
|
+
- `--show <name>` - Show name for the job (Default: from $SHOW or "default")
|
|
159
|
+
- `-s, --shot <name>` - Shot name for the job (Default: from $SHOT or "default")
|
|
160
|
+
- `--user <name>` - User name for the job (Default: from $USER or "unknown")
|
|
161
|
+
- `--job-name <name>` - Custom job name (Default: auto-generated)
|
|
162
|
+
|
|
163
|
+
## Examples
|
|
164
|
+
|
|
165
|
+
### Example 1: Simple Command Execution
|
|
166
|
+
```bash
|
|
167
|
+
# Create command file
|
|
168
|
+
cat > render_frames.txt << EOF
|
|
169
|
+
blender -b scene.blend -o /tmp/frame_#### -f 1
|
|
170
|
+
blender -b scene.blend -o /tmp/frame_#### -f 2
|
|
171
|
+
blender -b scene.blend -o /tmp/frame_#### -f 3
|
|
172
|
+
EOF
|
|
173
|
+
|
|
174
|
+
# Submit to OpenCue
|
|
175
|
+
cuecmd render_frames.txt
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Example 2: Chunked Execution
|
|
179
|
+
```bash
|
|
180
|
+
# Process 1000 images, 10 per frame
|
|
181
|
+
for i in {1..1000}; do
|
|
182
|
+
echo "convert input_$i.jpg -resize 50% output_$i.jpg"
|
|
183
|
+
done > convert_images.txt
|
|
184
|
+
|
|
185
|
+
cuecmd convert_images.txt --chunk 10
|
|
186
|
+
# This creates 100 frames, each processing 10 images
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Example 3: Resource-Intensive Jobs
|
|
190
|
+
```bash
|
|
191
|
+
# Heavy computation requiring more resources
|
|
192
|
+
cat > simulations.txt << EOF
|
|
193
|
+
python simulate.py --scene 1
|
|
194
|
+
python simulate.py --scene 2
|
|
195
|
+
python simulate.py --scene 3
|
|
196
|
+
EOF
|
|
197
|
+
|
|
198
|
+
cuecmd simulations.txt --cores 8 --memory 32
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Example 4: Testing Before Launch
|
|
202
|
+
```bash
|
|
203
|
+
# Test the job configuration without submitting
|
|
204
|
+
cuecmd commands.txt --chunk 5 --cores 2 --memory 4 --pretend
|
|
205
|
+
|
|
206
|
+
# Output shows:
|
|
207
|
+
# - Job name
|
|
208
|
+
# - Frame range
|
|
209
|
+
# - Resource requirements
|
|
210
|
+
# - Command file location
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Example 5: Custom Job Configuration
|
|
214
|
+
```bash
|
|
215
|
+
cuecmd commands.txt \
|
|
216
|
+
--show "my_project" \
|
|
217
|
+
--shot "sequence_010" \
|
|
218
|
+
--user "artist" \
|
|
219
|
+
--job-name "batch_processing" \
|
|
220
|
+
--chunk 10 \
|
|
221
|
+
--cores 4 \
|
|
222
|
+
--memory 8 \
|
|
223
|
+
--pause
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Example 6: Data Processing Pipeline
|
|
227
|
+
```bash
|
|
228
|
+
# Generate processing commands
|
|
229
|
+
cat > data_pipeline.txt << EOF
|
|
230
|
+
python extract.py data1.csv
|
|
231
|
+
python extract.py data2.csv
|
|
232
|
+
python extract.py data3.csv
|
|
233
|
+
python transform.py data1.json
|
|
234
|
+
python transform.py data2.json
|
|
235
|
+
python transform.py data3.json
|
|
236
|
+
EOF
|
|
237
|
+
|
|
238
|
+
cuecmd data_pipeline.txt --chunk 2 --cores 2 --memory 4
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Environment Variables
|
|
242
|
+
|
|
243
|
+
Cuecmd respects the following environment variables:
|
|
244
|
+
|
|
245
|
+
- `OPENCUE_HOSTS` - Comma-separated list of OpenCue servers
|
|
246
|
+
- `OPENCUE_FACILITY` - Default facility code
|
|
247
|
+
- `SHOW` - Default show name
|
|
248
|
+
- `SHOT` - Default shot name
|
|
249
|
+
- `USER` - Default user name
|
|
250
|
+
|
|
251
|
+
These can be overridden using command-line arguments.
|
|
252
|
+
|
|
253
|
+
## How It Works
|
|
254
|
+
|
|
255
|
+
1. **Command File Reading**: Cuecmd reads your text file containing commands
|
|
256
|
+
2. **Chunking**: Commands are divided into chunks based on the `--chunk` parameter
|
|
257
|
+
3. **Frame Range Calculation**: A frame range is calculated (1 to ceil(commands/chunk))
|
|
258
|
+
4. **Outline Creation**: An OpenCue outline is created with a Shell layer
|
|
259
|
+
5. **Job Submission**: The job is submitted to OpenCue for execution
|
|
260
|
+
6. **Frame Execution**: Each frame executes its assigned chunk of commands
|
|
261
|
+
|
|
262
|
+
### Technical Details
|
|
263
|
+
|
|
264
|
+
- Commands are copied to a temporary location for access during execution
|
|
265
|
+
- Each frame executes a helper script (`execute_commands.py`) that:
|
|
266
|
+
- Reads the command file
|
|
267
|
+
- Calculates which commands to run based on frame number and chunk size
|
|
268
|
+
- Executes each command in sequence
|
|
269
|
+
- Reports success/failure
|
|
270
|
+
|
|
271
|
+
## Running Tests
|
|
272
|
+
|
|
273
|
+
The `cuecmd` includes a comprehensive test suite.
|
|
274
|
+
|
|
275
|
+
### Quick Start
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
# Install with test dependencies
|
|
279
|
+
pip install -e ".[test]"
|
|
280
|
+
|
|
281
|
+
# Run all tests
|
|
282
|
+
pytest
|
|
283
|
+
|
|
284
|
+
# Run with coverage
|
|
285
|
+
pytest --cov=cuecmd --cov-report=term-missing
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Test Types
|
|
289
|
+
|
|
290
|
+
- **Unit tests** - Test individual functions (`tests/test_main.py`, `tests/test_execute_commands.py`)
|
|
291
|
+
- **Integration tests** - Test complete workflows (`tests/integration_tests.py`, `tests/test_integration_workflows.py`)
|
|
292
|
+
|
|
293
|
+
### Running Tests
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
# Basic test run
|
|
297
|
+
pytest tests/
|
|
298
|
+
|
|
299
|
+
# Verbose output
|
|
300
|
+
pytest -v
|
|
301
|
+
|
|
302
|
+
# Run specific test file
|
|
303
|
+
pytest tests/test_main.py
|
|
304
|
+
|
|
305
|
+
# Run with coverage and HTML report
|
|
306
|
+
pytest --cov=cuecmd --cov-report=html --cov-report=term-missing
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### CI/CD Integration
|
|
310
|
+
|
|
311
|
+
Cuecmd is integrated into OpenCue's CI/CD pipeline:
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
# In OpenCue root directory
|
|
315
|
+
./ci/run_python_tests.sh # Includes cuecmd tests
|
|
316
|
+
./ci/run_python_lint.sh # Includes cuecmd linting
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Docker Support
|
|
320
|
+
|
|
321
|
+
### Building the Docker Image
|
|
322
|
+
|
|
323
|
+
From the OpenCue root directory:
|
|
324
|
+
```bash
|
|
325
|
+
docker build -f cuecmd/Dockerfile -t cuecmd .
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Running the Docker Container
|
|
329
|
+
|
|
330
|
+
**Show help:**
|
|
331
|
+
```bash
|
|
332
|
+
docker run --rm cuecmd cuecmd --help
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
**Test with commands:**
|
|
336
|
+
```bash
|
|
337
|
+
# Create test commands
|
|
338
|
+
echo "echo 'test 1'" > /tmp/commands.txt
|
|
339
|
+
echo "echo 'test 2'" >> /tmp/commands.txt
|
|
340
|
+
|
|
341
|
+
# Run in pretend mode
|
|
342
|
+
docker run --rm -v /tmp:/data cuecmd cuecmd /data/commands.txt --pretend
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## Contributing
|
|
346
|
+
|
|
347
|
+
### Development Setup
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
# Clone and setup
|
|
351
|
+
git clone https://github.com/<username>/OpenCue.git
|
|
352
|
+
cd OpenCue/cuecmd
|
|
353
|
+
|
|
354
|
+
# Install with development dependencies
|
|
355
|
+
pip install -e ".[dev]"
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### Testing and Quality
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
# Run tests
|
|
362
|
+
pytest --cov=cuecmd --cov-report=term-missing
|
|
363
|
+
|
|
364
|
+
# Code formatting and linting
|
|
365
|
+
black cuecmd tests
|
|
366
|
+
isort cuecmd tests
|
|
367
|
+
pylint cuecmd tests
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Guidelines
|
|
371
|
+
|
|
372
|
+
- Add tests for new features
|
|
373
|
+
- Update documentation for API changes
|
|
374
|
+
- Ensure all tests pass before submitting PRs
|
|
375
|
+
|
|
376
|
+
## Troubleshooting
|
|
377
|
+
|
|
378
|
+
### Common Issues
|
|
379
|
+
|
|
380
|
+
**Command file not found:**
|
|
381
|
+
```bash
|
|
382
|
+
# Make sure file exists and path is correct
|
|
383
|
+
ls -l commands.txt
|
|
384
|
+
cuecmd $(pwd)/commands.txt # Use absolute path
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
**No commands found:**
|
|
388
|
+
```bash
|
|
389
|
+
# Check file has content and is not all blank lines
|
|
390
|
+
cat commands.txt
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
**Job fails to launch:**
|
|
394
|
+
```bash
|
|
395
|
+
# Check OpenCue connection
|
|
396
|
+
echo $OPENCUE_HOSTS
|
|
397
|
+
cueadmin -lj # Test connection
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
**Commands fail during execution:**
|
|
401
|
+
- Check command syntax is correct
|
|
402
|
+
- Verify commands have access to required files/paths
|
|
403
|
+
- Ensure sufficient resources (cores/memory) are allocated
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
cuecmd/__init__.py,sha256=Am7VgCcHrbnaZcPLWkkrjEUq3yPCCHhReTf0vCtGPns,1085
|
|
2
|
+
cuecmd/__main__.py,sha256=8Xqzz6hfotBFgEROLZKCmkmV43zoPiUkAuiBrOuu1eQ,1104
|
|
3
|
+
cuecmd/execute_commands.py,sha256=1PCNeSY13nnCbdb1IBN0z2SpPTjLExBMyOKhWqCXDA0,2243
|
|
4
|
+
cuecmd/main.py,sha256=Hs37g4YgKHgHq8ISP_p9ZA7goPQbHpCP4v6SNNV0DPY,7148
|
|
5
|
+
opencue_cuecmd-1.13.6.dist-info/METADATA,sha256=CbdDjU82UgVzVTuj9I1lAczTWeICE1Fjih4ONKjyTmU,10006
|
|
6
|
+
opencue_cuecmd-1.13.6.dist-info/WHEEL,sha256=KGYbc1zXlYddvwxnNty23BeaKzh7YuoSIvIMO4jEhvw,87
|
|
7
|
+
opencue_cuecmd-1.13.6.dist-info/entry_points.txt,sha256=-iHfU6EuZIXryO6cp0IqDWbBul8PJkB_Ohzg58CAjOw,48
|
|
8
|
+
opencue_cuecmd-1.13.6.dist-info/RECORD,,
|