QuAcc/remote.py

214 lines
5.3 KiB
Python
Raw Normal View History

2023-11-09 12:23:05 +01:00
import os
import queue
import stat
2023-11-26 16:34:49 +01:00
import subprocess
2023-11-09 12:23:05 +01:00
import threading
from itertools import product as itproduct
from os.path import expanduser
from pathlib import Path
2023-11-26 16:34:49 +01:00
from subprocess import DEVNULL, STDOUT
2023-11-09 12:23:05 +01:00
import paramiko
from tqdm import tqdm
known_hosts = Path(expanduser("~/.ssh/known_hosts"))
hostname = "ilona.isti.cnr.it"
username = "volpi"
__exec_main = "cd tesi; /home/volpi/.local/bin/poetry run main"
__exec_log = "/usr/bin/tail -f -n 0 tesi/quacc.log"
2023-11-22 19:27:09 +01:00
__log_file = "remote.log"
2023-11-09 12:23:05 +01:00
__target_dir = Path("/home/volpi/tesi")
__to_sync_up = {
"dir": [
"quacc",
"baselines",
2023-11-11 18:46:27 +01:00
"qcpanel",
2023-11-09 12:23:05 +01:00
],
"file": [
"conf.yaml",
"run.py",
2023-11-16 01:37:10 +01:00
"remote.py",
2023-11-23 00:26:22 +01:00
"merge_data.py",
2023-11-11 18:46:27 +01:00
"pyproject.toml",
2023-11-09 12:23:05 +01:00
],
}
__to_sync_down = {
"dir": [
"output",
],
"file": [],
}
def prune_remote(sftp: paramiko.SFTPClient, remote: Path):
_ex_list = []
mode = sftp.stat(str(remote)).st_mode
if stat.S_ISDIR(mode):
for f in sftp.listdir(str(remote)):
_ex_list.append([prune_remote, sftp, remote / f])
_ex_list.append([sftp.rmdir, str(remote)])
elif stat.S_ISREG(mode):
_ex_list.append([sftp.remove, str(remote)])
return _ex_list
def put_dir(sftp: paramiko.SFTPClient, from_: Path, to_: Path):
_ex_list = []
_ex_list.append([sftp.mkdir, str(to_)])
from_list = os.listdir(from_)
for f in from_list:
if (from_ / f).is_file():
_ex_list.append([sftp.put, str(from_ / f), str(to_ / f)])
elif (from_ / f).is_dir():
_ex_list += put_dir(sftp, from_ / f, to_ / f)
2023-11-11 18:46:27 +01:00
try:
to_list = sftp.listdir(str(to_))
for f in to_list:
if f not in from_list:
_ex_list += prune_remote(sftp, to_ / f)
except FileNotFoundError:
pass
2023-11-09 12:23:05 +01:00
return _ex_list
def get_dir(sftp: paramiko.SFTPClient, from_: Path, to_: Path):
_ex_list = []
if not (to_.exists() and to_.is_dir()):
_ex_list.append([os.mkdir, to_])
for f in sftp.listdir(str(from_)):
mode = sftp.stat(str(from_ / f)).st_mode
if stat.S_ISDIR(mode):
_ex_list += get_dir(sftp, from_ / f, to_ / f)
2023-11-11 18:46:27 +01:00
# _ex_list.append([sftp.rmdir, str(from_ / f)])
2023-11-09 12:23:05 +01:00
elif stat.S_ISREG(mode):
_ex_list.append([sftp.get, str(from_ / f), str(to_ / f)])
2023-11-11 18:46:27 +01:00
# _ex_list.append([sftp.remove, str(from_ / f)])
2023-11-09 12:23:05 +01:00
return _ex_list
def sync_code(*, ssh: paramiko.SSHClient = None, verbose=False):
_was_ssh = ssh is not None
if ssh is None:
ssh = paramiko.SSHClient()
ssh.load_host_keys(known_hosts)
ssh.connect(hostname=hostname, username=username)
sftp = ssh.open_sftp()
to_move = [item for k, vs in __to_sync_up.items() for item in itproduct([k], vs)]
_ex_list = []
for mode, f in to_move:
from_ = Path(f).absolute()
to_ = __target_dir / f
if mode == "dir":
_ex_list += put_dir(sftp, from_, to_)
elif mode == "file":
_ex_list.append([sftp.put, str(from_), str(to_)])
for _ex in tqdm(_ex_list, desc="synching code: "):
fn_ = _ex[0]
try:
fn_(*_ex[1:])
except IOError:
if verbose:
print(f"Info: directory {to_} already exists.")
sftp.close()
if not _was_ssh:
ssh.close()
def sync_output(*, ssh: paramiko.SSHClient = None):
_was_ssh = ssh is not None
if ssh is None:
ssh = paramiko.SSHClient()
ssh.load_host_keys(known_hosts)
ssh.connect(hostname=hostname, username=username)
sftp = ssh.open_sftp()
to_move = [item for k, vs in __to_sync_down.items() for item in itproduct([k], vs)]
_ex_list = []
for mode, f in to_move:
from_ = __target_dir / f
to_ = Path(f).absolute()
if mode == "dir":
_ex_list += get_dir(sftp, from_, to_)
elif mode == "file":
_ex_list.append([sftp.get, str(from_), str(to_)])
for _ex in tqdm(_ex_list, desc="synching output: "):
fn_ = _ex[0]
fn_(*_ex[1:])
sftp.close()
if not _was_ssh:
ssh.close()
def _echo_channel(ch: paramiko.ChannelFile):
while line := ch.readline():
print(line, end="")
def _echo_log(ssh: paramiko.SSHClient, q_: queue.Queue):
_, rout, _ = ssh.exec_command(__exec_log, timeout=5.0)
while True:
try:
2023-11-22 19:27:09 +01:00
_line = rout.readline()
2023-11-09 12:23:05 +01:00
with open(__log_file, "a") as f:
2023-11-22 19:27:09 +01:00
f.write(_line)
2023-11-09 12:23:05 +01:00
except TimeoutError:
pass
try:
q_.get_nowait()
return
except queue.Empty:
pass
2023-11-22 19:27:09 +01:00
def remote(detatch=False):
2023-11-09 12:23:05 +01:00
ssh = paramiko.SSHClient()
ssh.load_host_keys(known_hosts)
ssh.connect(hostname=hostname, username=username)
sync_code(ssh=ssh)
2023-11-22 19:27:09 +01:00
__to_exec = __exec_main
if detatch:
__to_exec += " &> out & disown"
_, rout, rerr = ssh.exec_command(__to_exec)
if detatch:
ssh.close()
return
2023-11-09 12:23:05 +01:00
2023-11-22 19:27:09 +01:00
q = queue.Queue()
2023-11-09 12:23:05 +01:00
_tlog = threading.Thread(target=_echo_log, args=[ssh, q])
_tlog.start()
_tchans = [threading.Thread(target=_echo_channel, args=[ch]) for ch in [rout, rerr]]
for th in _tchans:
th.start()
for th in _tchans:
th.join()
q.put(None)
sync_output(ssh=ssh)
2023-11-22 19:27:09 +01:00
_tlog.join()
ssh.close()