Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP - Support non-standard remote run directories. #2252

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions lib/python/rose/opt_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,16 @@ class RoseOptionParser(OptionParser):
{"action": "store",
"metavar": "NAME",
"help": "Specify the suite name."}],
"run_dir": [
["--run-dir"],
{"action": "store",
"metavar": "DIR",
"help": "Suite run directory."}],
"work_dir": [
["--work-dir"],
{"action": "store",
"metavar": "DIR",
"help": "Suite work and share directory."}],
"new_mode": [
["--new", "-N"],
{"action": "store_true",
Expand Down
15 changes: 15 additions & 0 deletions lib/python/rose/suite_engine_procs/cylc.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,21 @@ def __init__(self, *args, **kwargs):
self.host = None
self.user = None

def get_suite_run_work_dir(self, item, suite_name, host, user):
"""Get suite run or work (item) directory on given host."""
try:
out = self.popen(
"cylc", "get-global-config", "-i",
"[hosts][%s]%s directory" % (host, item))[0]
except RosePopenError:
out = self.SUITE_DIR_REL_ROOT
the_d = out.splitlines()[0]
# TODO - DOCUMENT ONLY '$USER' is replaced in 'run directory'
# (env vars would have to be evaluated on the remote host).
# (can be done for rose-suite-run remote, but not rsync!).
the_d = the_d.replace('$USER', user)
return "%s/%s" % (the_d, suite_name)

def check_global_conf_compat(self):
"""Raise exception on incompatible Cylc global configuration."""
expected = os.path.join("~", self.SUITE_DIR_REL_ROOT)
Expand Down
57 changes: 45 additions & 12 deletions lib/python/rose/suite_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ class SuiteRunner(Runner):
"log_keep",
"log_name",
"name",
"run_dir",
"work_dir",
"new_mode",
"no_overwrite_mode",
"opt_conf_keys",
Expand Down Expand Up @@ -113,6 +115,16 @@ def __init__(self, *args, **kwargs):
host_selector=self.host_selector,
suite_engine_proc=self.suite_engine_proc)

@staticmethod
def _auth_split(auth):
"""Return (user, host) from "[user@]host", defaulting to $USER."""
host = auth
if "@" in auth:
user, host = auth.split("@", 1)
else:
user = os.environ['USER']
return (user, host)

def run_impl(self, opts, args, uuid, work_files):
# Log file, temporary
if hasattr(self.event_handler, "contexts"):
Expand All @@ -132,7 +144,6 @@ def run_impl(self, opts, args, uuid, work_files):
if opts.remote:
# opts.name always set for remote.
return self._run_remote(opts, opts.name)

conf_tree = self.config_load(opts)
self.fs_util.chdir(conf_tree.conf_dirs[0])

Expand Down Expand Up @@ -216,6 +227,7 @@ def run_impl(self, opts, args, uuid, work_files):
suite_dir = os.path.join(temp_dir, suite_dir_rel)
os.makedirs(suite_dir, 0o0700)
else:
# TODO - LOCALHOST ONLY (leave as ~?)
suite_dir = os.path.join(
os.path.expanduser("~"), suite_dir_rel)

Expand Down Expand Up @@ -356,9 +368,7 @@ def run_impl(self, opts, args, uuid, work_files):
auths = self.suite_engine_proc.get_tasks_auths(suite_name)
proc_queue = [] # [[proc, command, "ssh"|"rsync", auth], ...]
for auth in sorted(auths):
host = auth
if "@" in auth:
host = auth.split("@", 1)[1]
user, host = self._auth_split(auth)
# Remote shell
command = self.popen.get_cmd("ssh", "-n", auth)
# Provide ROSE_VERSION and CYLC_VERSION in the environment
Expand All @@ -375,6 +385,11 @@ def run_impl(self, opts, args, uuid, work_files):
default="rose")
# Build remote "rose suite-run" command
shcommand += " %s suite-run -vv -n %s" % (rose_bin, suite_name)
shcommand += " --run-dir=%s --work-dir=%s" % (
self.suite_engine_proc.get_suite_run_work_dir(
"run", suite_name, host, user),
self.suite_engine_proc.get_suite_run_work_dir(
"work", suite_name, host, user))
for key in ["new", "debug", "install-only"]:
attr = key.replace("-", "_") + "_mode"
if getattr(opts, attr, None) is not None:
Expand Down Expand Up @@ -429,7 +444,10 @@ def run_impl(self, opts, args, uuid, work_files):
filters = {"excludes": [], "includes": []}
for name in ["", "log/", "share/", "share/cycle/", "work/"]:
filters["excludes"].append(name + uuid)
target = auth + ":" + suite_dir_rel
user, host = self._auth_split(auth)
target = (auth + ":" +
self.suite_engine_proc.get_suite_run_work_dir(
"run", suite_name, host, user))
cmd = self._get_cmd_rsync(target, **filters)
proc_queue.append(
[self.popen.run_bg(*cmd), cmd, "rsync", auth])
Expand Down Expand Up @@ -496,18 +514,25 @@ def _run_conf(
def _run_init_dir(self, opts, suite_name, conf_tree=None, r_opts=None,
locs_conf=None):
"""Create the suite's directory."""
suite_dir_rel = self._suite_dir_rel(suite_name)
home = os.path.expanduser("~")
if opts.run_dir:
suite_dir_home = opts.run_dir
else:
suite_dir_rel = self._suite_dir_rel(suite_name)
home = os.path.expanduser("~")
suite_dir_home = os.path.join(home, suite_dir_rel)
suite_dir_root = self._run_conf("root-dir", conf_tree=conf_tree,
r_opts=r_opts)
if suite_dir_root:
if locs_conf is not None:
locs_conf.set(["localhost", "root-dir"], suite_dir_root)
suite_dir_root = env_var_process(suite_dir_root)
suite_dir_home = os.path.join(home, suite_dir_rel)
if (suite_dir_root and
os.path.realpath(home) != os.path.realpath(suite_dir_root)):
suite_dir_real = os.path.join(suite_dir_root, suite_dir_rel)
os.path.realpath(suite_dir_home) !=
os.path.realpath(suite_dir_root)):
if opts.run_dir:
suite_dir_real = os.path.join(suite_dir_root, suite_name)

Choose a reason for hiding this comment

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

@matthewrmshin I believe at this point, suite_dir_root is Rose config item root-dir, and suite_name is appropriately set - however neither of these add {pre,suf}fix 'cylc-run' - so if opts.run_dir is set, suite_dir_real = <rose root-dir>/<suite-name> (no /cylc-run/ in between) which is then symlinked to ~/cylc-run which is the exact behaviour I'm seeing.

else:
suite_dir_real = os.path.join(suite_dir_root, suite_dir_rel)
self.fs_util.makedirs(suite_dir_real)
self.fs_util.symlink(suite_dir_real, suite_dir_home,
opts.no_overwrite_mode)
Expand Down Expand Up @@ -595,7 +620,11 @@ def _run_init_dir_work(self, opts, suite_name, name, conf_tree=None,
if locs_conf is not None:
locs_conf.set(["localhost", key], item_root)
item_root = env_var_process(item_root)
suite_dir_rel = self._suite_dir_rel(suite_name)
if (opts.work_dir and name == "work" or
opts.run_dir and name == "share"):
suite_dir_rel = suite_name
else:
suite_dir_rel = self._suite_dir_rel(suite_name)
if os.path.isabs(item_root):
item_path_source = os.path.join(item_root, suite_dir_rel, name)
else:
Expand Down Expand Up @@ -637,13 +666,17 @@ def _run_remote(self, opts, suite_name):
self.fs_util.delete(suite_dir_rel)
if opts.run_mode == "run" or not os.path.exists(suite_dir_rel):
self._run_init_dir(opts, suite_name, r_opts=r_opts)
os.chdir(suite_dir_rel)
# make work and share dirs relative to work_dir
self.fs_util.makedirs(opts.work_dir)
os.chdir(opts.work_dir)
for name in ["share", "share/cycle", "work"]:
uuid_file = os.path.join(name, r_opts["uuid"])
if os.path.exists(uuid_file):
self.handle_event(name + "/" + r_opts["uuid"] + "\n", level=0)
else:
self._run_init_dir_work(opts, suite_name, name, r_opts=r_opts)
# make log dirs relative to run_dir
os.chdir(opts.run_dir)
if not opts.install_only_mode:
uuid_file = os.path.join("log", r_opts["uuid"])
if os.path.exists(uuid_file):
Expand Down