Skip to content

Commit 02720ef

Browse files
authored
Improvements to mlc remote_run (#741)
* Pass --quiet for get,docker * Improve remote_run
1 parent 2c46828 commit 02720ef

File tree

4 files changed

+222
-9
lines changed

4 files changed

+222
-9
lines changed

automation/script/docker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ def docker_run(self_module, i):
388388

389389
# Ensure Docker is available
390390
r = self_module.action_object.access(
391-
{'action': 'run', 'automation': 'script', 'tags': "get,docker"})
391+
{'action': 'run', 'automation': 'script', 'tags': "get,docker", 'quiet': True})
392392
if r['return'] > 0:
393393
return r
394394

automation/script/remote_run.py

Lines changed: 216 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def remote_run(self_module, i):
3030
logger = self_module.logger
3131
env = i.get('env', {})
3232
remote_host = i.get('remote_host', 'localhost')
33-
remote_port = i.get('remote_port', 22)
33+
remote_port = i.get('remote_port', '22')
3434

3535
prune_result = prune_input(
3636
{'input': i, 'extra_keys_starts_with': ['remote_']})
@@ -40,6 +40,7 @@ def remote_run(self_module, i):
4040
run_input = prune_result['new_input']
4141
mlc_run_cmd = run_input['mlc_run_cmd']
4242

43+
# print(script_cmd)
4344
cur_dir = os.getcwd()
4445

4546
r = self_module._select_script(i)
@@ -59,10 +60,20 @@ def remote_run(self_module, i):
5960
if r['return'] > 0:
6061
return r
6162

63+
remote_env = {}
64+
6265
remote_run_settings = r['remote_run_settings']
6366
env = r['env']
67+
state = r['state']
68+
meta = r['meta']
69+
70+
r = call_remote_run_prepare(self_module, meta, script, env, state, i)
71+
if r['return'] > 0:
72+
return r
73+
74+
files_to_copy = r.get('files_to_copy', [])
75+
remote_env = r.get('remote_env', {})
6476

65-
# Execute the experiment script
6677
mlc_script_input = {
6778
'action': 'run', 'target': 'script'
6879
}
@@ -73,23 +84,50 @@ def remote_run(self_module, i):
7384
if i.get('remote_pull_mlc_repos', False):
7485
run_cmds.append("mlc pull repo")
7586

76-
files_to_copy = []
7787
env_keys_to_copy = remote_run_settings.get('env_keys_to_copy', [])
88+
input_mapping = meta.get('input_mapping', {})
89+
7890
for key in env_keys_to_copy:
7991
if key in env and os.path.exists(env[key]):
8092
files_to_copy.append(env[key])
93+
remote_env[key] = os.path.join(
94+
"mlc-remote-artifacts",
95+
os.path.basename(
96+
env[key]))
97+
98+
for k, value in input_mapping.items():
99+
if value == key and k in run_input:
100+
run_input[k] = remote_env[key]
101+
102+
i_copy = copy.deepcopy(i)
103+
i_copy['run_cmd'] = run_input
104+
105+
r = regenerate_script_cmd(i_copy)
106+
if r['return'] > 0:
107+
return r
108+
109+
# " ".join(mlc_run_cmd.split(" ")[1:])
110+
script_run_cmd = r['run_cmd_string']
111+
112+
if remote_env:
113+
for key in remote_env:
114+
script_run_cmd += f" --env.{key}={remote_env[key]}"
81115

82-
script_run_cmd = " ".join(mlc_run_cmd.split(" ")[1:])
83-
run_cmds.append(f"mlcr {script_run_cmd}")
116+
run_cmds.append(f"{script_run_cmd}")
84117

85118
remote_inputs = {}
119+
86120
for key in ["host", "port", "user", "client_refresh",
87-
"password", "skip_host_verify", "ssh_key_file"]:
121+
"password", "skip_host_verify", "ssh_key_file", "copy_directory"]:
88122
if i.get(f"remote_{key}"):
89123
remote_inputs[key] = i[f"remote_{key}"]
90124

91125
if files_to_copy:
126+
remote_copy_directory = i.get(
127+
"remote_copy_directory",
128+
"mlc-remote-artifacts")
92129
remote_inputs['files_to_copy'] = files_to_copy
130+
remote_inputs['copy_directory'] = remote_copy_directory
93131

94132
# Execute the remote command
95133
mlc_remote_input = {
@@ -170,4 +208,175 @@ def update_meta_for_selected_variations(self_module, script, input_params):
170208
# Set Docker-specific configurations
171209
remote_run_settings = state_data['remote_run']
172210
return {'return': 0, 'remote_run_settings': remote_run_settings,
173-
'env': env, 'state': state_data}
211+
'env': env, 'state': state_data, 'meta': metadata}
212+
213+
214+
def call_remote_run_prepare(self_module, meta, script_item, env, state, i):
215+
216+
path_to_customize_py = os.path.join(script_item.path, 'customize.py')
217+
logger = self_module.logger
218+
recursion_spaces = ''
219+
220+
# Check and run remote_run_prepare in customize.py
221+
if os.path.isfile(path_to_customize_py):
222+
r = utils.load_python_module(
223+
{'path': script_item.path, 'name': 'customize'})
224+
if r['return'] > 0:
225+
return r
226+
227+
customize_code = r['code']
228+
229+
customize_common_input = {
230+
'input': i,
231+
'automation': self_module,
232+
'artifact': script_item,
233+
# 'customize': script_item.meta.get('customize', {}),
234+
# 'os_info': os_info,
235+
# 'recursion_spaces': recursion_spaces,
236+
# 'script_tags': script_tags,
237+
# 'variation_tags': variation_tags
238+
}
239+
run_script_input = {}
240+
run_script_input['customize_code'] = customize_code
241+
run_script_input['customize_common_input'] = customize_common_input
242+
243+
if 'remote_run_prepare' in dir(customize_code):
244+
245+
logger.debug(
246+
recursion_spaces +
247+
' - Running remote_run_prepare ...')
248+
249+
run_script_input['run_state'] = {}
250+
251+
ii = copy.deepcopy(customize_common_input)
252+
ii['env'] = env
253+
ii['state'] = state
254+
ii['meta'] = meta
255+
ii['automation'] = self_module
256+
# may need to detect versions in multiple paths
257+
ii['run_script_input'] = run_script_input
258+
259+
r = customize_code.remote_run_prepare(ii)
260+
return r
261+
262+
return {'return': 0}
263+
264+
265+
def regenerate_script_cmd(i):
266+
267+
remote_run_settings = i.get('remote_run_settings', {})
268+
fake_run = i.get('fake_run', False)
269+
270+
i_run_cmd = i['run_cmd']
271+
272+
# Remove environment variables with host path values
273+
if 'env' in i_run_cmd:
274+
env = i_run_cmd['env']
275+
for key in list(env):
276+
value = env[key]
277+
278+
# Check if the value is a string containing the specified paths
279+
if isinstance(value, str) and (
280+
os.path.join("local", "cache", "") in value or
281+
os.path.join("MLC", "repos", "") in value or
282+
"<<<" in value
283+
):
284+
del env[key]
285+
286+
# Check if the value is a list and remove matching items
287+
elif isinstance(value, list):
288+
# Identify values to remove
289+
values_to_remove = [
290+
val for val in value
291+
if isinstance(val, str) and (
292+
os.path.join("local", "cache", "") in val or
293+
os.path.join("MLC", "repos", "") in val or
294+
"<<<" in value
295+
)
296+
]
297+
298+
# Remove key if all values match; otherwise, filter the list
299+
if len(values_to_remove) == len(value):
300+
del env[key]
301+
else:
302+
env[key] = [
303+
val for val in value if val not in values_to_remove]
304+
305+
# docker_run_cmd_prefix = i.get('docker_run_cmd_prefix', '')
306+
307+
# Regenerate command from dictionary input
308+
run_cmd = 'mlcr'
309+
310+
skip_input_for_fake_run = remote_run_settings.get(
311+
'skip_input_for_fake_run', [])
312+
add_quotes_to_keys = remote_run_settings.get('add_quotes_to_keys', [])
313+
314+
def rebuild_flags(
315+
command_dict,
316+
is_fake_run,
317+
skip_keys_for_fake_run,
318+
quote_keys,
319+
prefix
320+
):
321+
"""
322+
Recursively rebuilds command-line flags from a dictionary of inputs.
323+
324+
:param command_dict: Dictionary containing command-line keys and values.
325+
:param is_fake_run: Boolean indicating if this is a fake run.
326+
:param skip_keys_for_fake_run: List of keys to skip in fake run mode.
327+
:param quote_keys: List of keys that require values to be quoted.
328+
:param prefix: String to prepend to keys for hierarchical keys.
329+
:return: A reconstructed command-line string.
330+
"""
331+
command_line = ""
332+
333+
# Sort keys to ensure 'tags' appears first if present.
334+
keys = sorted(command_dict.keys(), key=lambda x: x != "tags")
335+
336+
for key in keys:
337+
if key in ["input", "output", "outdirname"]:
338+
continue # We have the corresponding env keys in container env string
339+
# Construct the full key with the prefix.
340+
full_key = f"{prefix}.{key}" if prefix else key
341+
342+
# Skip keys marked for exclusion in fake run mode.
343+
if is_fake_run and full_key in skip_keys_for_fake_run:
344+
continue
345+
346+
value = command_dict[key]
347+
quote = '"' if full_key in quote_keys else ""
348+
349+
# Recursively process nested dictionaries.
350+
if isinstance(value, dict):
351+
command_line += rebuild_flags(
352+
value,
353+
is_fake_run,
354+
skip_keys_for_fake_run,
355+
quote_keys,
356+
full_key
357+
)
358+
# Process lists by concatenating values with commas.
359+
elif isinstance(value, list):
360+
list_values = ",".join(
361+
quote_if_needed(
362+
item, quote) for item in value)
363+
command_line += f" --{full_key},={list_values}"
364+
# Process scalar values.
365+
else:
366+
if full_key in ['s', 'v']:
367+
command_line += f" -{full_key}"
368+
else:
369+
command_line += f" --{full_key}={quote_if_needed(value, quote)}"
370+
371+
return command_line
372+
373+
run_cmd += rebuild_flags(i_run_cmd,
374+
fake_run,
375+
skip_input_for_fake_run,
376+
add_quotes_to_keys,
377+
'')
378+
379+
# run_cmd = docker_run_cmd_prefix + ' && ' + \
380+
# run_cmd if docker_run_cmd_prefix != '' else run_cmd
381+
382+
return {'return': 0, 'run_cmd_string': run_cmd}

script/remote-run-commands/customize.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,16 @@ def preprocess(i):
5959
if password:
6060
rsync_base = ["sshpass", "-p", password] + rsync_base
6161

62+
target_directory = env['MLC_SSH_TARGET_COPY_DIRECTORY']
63+
6264
# ---- Execute copy commands ----
6365
for file in files_to_copy:
6466
cmd = [
6567
"rsync",
6668
"-avz",
6769
"-e", " ".join(ssh_cmd), # rsync expects a single string here
6870
file,
69-
f"{user}@{host}:",
71+
f"{user}@{host}:{target_directory}/"
7072
]
7173

7274
print("Executing:", " ".join(cmd))

script/remote-run-commands/meta.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ default_env:
77
MLC_SSH_HOST: localhost
88
MLC_SSH_KEY_FILE: <<<HOME>>>/.ssh/id_rsa
99
MLC_SSH_PORT: '22'
10+
MLC_SSH_TARGET_COPY_DIRECTORY: '.'
1011
input_mapping:
1112
client_refresh: MLC_SSH_CLIENT_REFRESH
1213
host: MLC_SSH_HOST
@@ -17,6 +18,7 @@ input_mapping:
1718
skip_host_verify: MLC_SSH_SKIP_HOST_VERIFY
1819
ssh_key_file: MLC_SSH_KEY_FILE
1920
user: MLC_SSH_USER
21+
copy_directory: MLC_SSH_TARGET_COPY_DIRECTORY
2022
tags:
2123
- remote
2224
- run

0 commit comments

Comments
 (0)