diff --git a/tes/sshwrapper/__init__.py b/tes/sshwrapper/__init__.py index 2dc0160e9ca736be61158d2068228dcb6bef3293..ed80faba085f8d3cf065826963d0356b72884d80 100644 --- a/tes/sshwrapper/__init__.py +++ b/tes/sshwrapper/__init__.py @@ -23,6 +23,7 @@ class SshCtrlException(Exception): """ pass + class Ssh: """ Ssh class can execute or create tunnelstat @@ -74,6 +75,8 @@ class Ssh: """ import os import stat + import logging + logger = logging.getLogger() ctrlsocket = "/tmp/cm-{}-{}".format(user,host) try: mode = os.stat(ctrlsocket).st_mode @@ -81,11 +84,10 @@ class Ssh: mode = None if mode is None: - ctrlsocket = "/tmp/cm-{}-{}".format(user,host) sshcmd = ["ssh", '-o', 'StrictHostKeyChecking=no', "-S", ctrlsocket, "-M", '-o', 'ControlPersist=10m', - '-l', user, host] + '-N','-l', user, host] env = os.environ.copy() if sess.socket is None: raise SshAgentException("No ssh-agent yet") @@ -93,16 +95,94 @@ class Ssh: ctrl_p = subprocess.Popen(sshcmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - stdin=subprocess.PIPE, env=env) + env=env) + stdout = ctrl_p.communicate() + logger.debug('communicate on the control port complete') + sess.pids.append(ctrl_p.pid) try: mode = os.stat(ctrlsocket).st_mode except FileNotFoundError: mode = None + logger.debug("control socket not open") + logger.error(ctrl_p.stderr.read()) + raise SshCtrlException() if not stat.S_ISSOCK(mode): raise SshCtrlException() return ctrlsocket + @staticmethod + def parse_sftp_output(output): + import logging + logger = logging.getLogger() + rv = [] + pwd = None + import dateutil.parser + for l in output.splitlines(): + + fields = l.split(None,8) + fd = {} + if len(fields) == 9: + + fd['name'] = fields[8] + fd['mode'] = fields[0] + fd['hardlinks'] = fields[1] + fd['user'] = fields[2] + fd['group'] = fields[3] + fd['size'] = fields[4] + fd['mtime'] = dateutil.parser.parse("{} {} {}".format(fields[5],fields[6],fields[7])).isoformat() + fd['name'] = fields[8] + rv.append(fd) + remotestr = "Remote working directory: " + if remotestr in l: + pwd = l[len(remotestr):].rstrip() + return ({'pwd':pwd,'files':rv}) + + + @staticmethod + def sftpls(sess, host, user, path="",changepath="."): + """ + Use sftp to run an ls on the given path. + Return the directory listing (as a list) and the cwd + """ + import os + import logging + logger = logging.getLogger() + env = os.environ.copy() + if sess.socket is None: + raise SshAgentException("No ssh-agent yet") + env['SSH_AUTH_SOCK'] = sess.socket + Ssh.validate_username(user) + Ssh.validate_hostname(host) + ctrlsocket = Ssh.get_ctrl_master_socket(sess, host, user) + exec_p = subprocess.Popen(['sftp', '-b', '-','-o', 'Stricthostkeychecking=no', + '-o', 'ControlPath={}'.format(ctrlsocket), + '{}@{}'.format(user, host)], + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + stdin=subprocess.PIPE, env=env) + + sftpcmd="cd {}\n cd {}\n pwd\nls -al\n".format(path,changepath) + + (stdout, stderr) = exec_p.communicate(sftpcmd.encode()) + if stderr is not None: + if not (stderr == b''): + logger.error('sftp failure') + if ('Couldn\'t canonicalize: No such file or directory' in stderr.decode()): + logger.error('can\'t change to that directory') + return Ssh.sftpls(sess,host,user,path,changepath='.') + if ('Permission denied' in stderr.decode()): + logger.error('can\'t change to that directory') + return Ssh.sftpls(sess,host,user,path,changepath='.') + + logger.error(stderr.decode()) + logger.error(('Permission denied' in stderr.decode())) + logger.error('Couldn\'t canonicalize: No such file or directory' in stderr.decode()) + raise SshCtrlException() + + dirlist = Ssh.parse_sftp_output(stdout.decode()) + return dirlist + + @staticmethod def execute(sess, host, user, cmd, stdin=None): """ diff --git a/tes/tunnelstat/__init__.py b/tes/tunnelstat/__init__.py index de537d5fdbbc74785380527cc62c28b03bb00bbd..c6b9b5b64083a9d1692d098ae940287390a55be0 100644 --- a/tes/tunnelstat/__init__.py +++ b/tes/tunnelstat/__init__.py @@ -89,6 +89,8 @@ class SSHSession: keygenout,keygenerr = p.communicate(l) certcontents = SSHSession.parse_cert_contents(keygenout.decode().splitlines()) res.append(certcontents) + else: + res.append({'pubkey':l.decode()}) return res @staticmethod