'''siemens_to_ismrmrd client.'''
import uuid
import os
import logging
from tempfile import NamedTemporaryFile
import warnings
import paramiko
from tqdm import tqdm
with warnings.catch_warnings():
warnings.filterwarnings('ignore', category=FutureWarning)
import ismrmrd
from mr_utils.config import ProfileConfig
[docs]class TqdmWrap(tqdm):
[docs] def viewBar(self, a, b):
'''Monitor progress of sftp transfers'''
self.total = int(b)
# update pbar with increment
self.update(int(a - self.n))
[docs]class FastTransport(paramiko.Transport):
def __init__(self, sock):
'''Increase window size in hopes to go faster...'''
super(FastTransport, self).__init__(sock)
self.window_size = 2147483647
self.packetizer.REKEY_BYTES = pow(2, 40)
self.packetizer.REKEY_PACKETS = pow(2, 40)
[docs]def s2i_client(
filename,
put_file=True,
get_file=True,
cleanup_raw=True,
cleanup_processed=True,
remote_dir='/tmp',
host=None,
port=22,
username=None,
ssh_key=None,
password=None,
debug_level=logging.INFO):
'''Runs siemens_to_ismrmrd on a remote computer.
Main idea: allow users to use siemens_to_ismrmrd even if they don't have
it installed locally. They will, however, require SSH access to computer
that does have it installed.
Client puts file on server using SFTP, runs siemens_to_ismrmrd over SSH,
and gets the file back using SFTP. Username, password, hostname, and port
is retrieved from the active profile in profiles.config. Default port is
22. If no password is found, the RSA SSH key will be used from either the
specified directory in profiles.config or, if empty, use '~/.ssh/id_rsa'.
Parameters
==========
filename : str
Raw data (.dat) file on the local machine (if put_file is True)
or on the remote machine (if put_file is False).
put_file : bool, optional
Whether or not to copy the raw data file from local to remote.
get_file : bool, optional
Whether or not to copy the processed file from machine to local.
cleanup_raw : bool, optional
Whether or not to delete raw data on remote.
cleanup_processed : bool, optional
Whether or not to delete processed data on remote.
remote_dir : str, optional
Working directory on remote (default in /tmp).
host : str, optional
hostname of remote machine.
port : int, optional
Port of remote machine to connect to.
username : str, optional
Username to use for SSH/SFTP connections.
ssh_key : str, optional
RSA private key file to use for SSH/SFTP connections.
password : str, optional
Password to use fr SSH/SFTP connections (stored in plaintext).
debug_level : logging_level, optional
Level of verbosity; see python logging module.
Returns
=======
dset : ismrmrd.Dataset
Result of siemens_to_ismrmrd
'''
# Setup logging
logging.basicConfig(format='%(levelname)s: %(message)s', level=debug_level)
# Grab credentials
profile = ProfileConfig()
if host is None:
host = profile.get_config_val('siemens_to_ismrmrd.host')
logging.info('profiles.config: using hostname %s', host)
if port is None:
host = profile.get_config_val('siemens_to_ismrmrd.port')
logging.info('profiles.config: using port %s', str(port))
if username is None:
username = profile.get_config_val('siemens_to_ismrmrd.user')
logging.info('profiles.config: using username %s', username)
if password is None:
password = profile.get_config_val('siemens_to_ismrmrd.password')
# If blank, assume no password
if password == '':
password = None
else:
logging.warning(
'profiles.config: using password stored in plaintext!')
logging.warning('Suggested to use RSA key for connections!')
# So now look for the RSA key
if ssh_key is None:
ssh_key = profile.get_config_val('siemens_to_ismrmrd.ssh_key')
if ssh_key == '':
ssh_key = '%s/.ssh/id_rsa' % os.environ['HOME']
logging.info('Using defaut RSA key %s', ssh_key)
else:
logging.info('profiles.config: using RSA key %s', ssh_key)
try:
ssh_conn = FastTransport((host, port))
ssh_conn.use_compression(True)
if ssh_key is not None:
ssh_conn.connect(
pkey=paramiko.RSAKey.from_private_key_file(ssh_key),
username=username)
else:
ssh_conn.connect(username=username, password=password)
sftp = paramiko.SFTPClient.from_transport(ssh_conn)
if put_file:
# Upload file to the server
remote_filename = '%s/siemens_to_ismrmrd_%s' % (
remote_dir, str(uuid.uuid4()))
logging.info('Starting transfer of %s to %s:%s:%s...', filename,
host, str(port), remote_filename)
with TqdmWrap(ascii=True, unit='b', unit_scale=True) as pbar:
# this can be pretty slow
sftp.put(filename, remote_filename, callback=pbar.viewBar)
else:
# No file to transfer, then the filename is the name of the file
# we need in the working directory on the remote.
remote_filename = '%s/%s' % (remote_dir, filename)
logging.info(
'Not transferring, looking for remote file %s:%s:%s', host,
str(port), remote_filename)
# Run siemens_to_ismrmrd on remote
ssh_client = paramiko.SSHClient()
ssh_client.load_system_host_keys()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
if ssh_key is not None:
ssh_client.connect(hostname=host, port=port, username=username)
else:
ssh_client.connect(hostname=host, port=port, username=username,
password=password)
logging.info('SSH client succesfully connected!')
processed_filename = '%s_processed' % remote_filename
cmd = 'siemens_to_ismrmrd -f %s -o %s' % (
remote_filename, processed_filename)
logging.info('Running \"%s\" on remote...', cmd)
_stdin, stdout, stderr = ssh_client.exec_command(cmd)
for line in stdout.read().decode().splitlines():
logging.info(line)
if stderr.readlines():
logging.error(stderr.read().decode())
# Copy the processed file
if get_file:
tmp_name = NamedTemporaryFile().name
logging.info(
'Transferring processed file back to local machine...')
with TqdmWrap(ascii=True, unit='b', unit_scale=True) as pbar:
sftp.get(processed_filename, tmp_name, callback=pbar.viewBar)
dset = ismrmrd.Dataset(tmp_name, '/dataset', False)
else:
logging.info('Not transferring processed file back from remote')
dset = None
# Clean files from server
if cleanup_raw:
logging.info('Cleaning up raw data on remote')
sftp.remove(remote_filename)
if cleanup_processed:
logging.info('Cleaning up processed data on remote')
sftp.remove(processed_filename)
finally:
ssh_client.close()
sftp.close()
ssh_conn.close()
return dset
if __name__ == '__main__':
pass