spug/spug_api/libs/ssh.py

83 lines
2.8 KiB
Python
Raw Normal View History

2019-11-11 01:16:18 +00:00
from paramiko.client import SSHClient, AutoAddPolicy
from paramiko.config import SSH_PORT
from paramiko.rsakey import RSAKey
2019-11-18 13:04:03 +00:00
from paramiko.ssh_exception import AuthenticationException
2019-11-11 01:16:18 +00:00
from io import StringIO
class SSH:
2019-11-18 13:04:03 +00:00
def __init__(self, hostname, port=SSH_PORT, username='root', pkey=None, password=None, connect_timeout=10):
2019-11-11 01:16:18 +00:00
if pkey is None and password is None:
raise Exception('public key and password must have one is not None')
self.client = None
self.arguments = {
2019-11-18 13:04:03 +00:00
'hostname': hostname,
2019-11-11 01:16:18 +00:00
'port': port,
'username': username,
'password': password,
2019-11-18 13:04:03 +00:00
'pkey': RSAKey.from_private_key(StringIO(pkey)) if isinstance(pkey, str) else pkey,
2019-11-11 01:16:18 +00:00
'timeout': connect_timeout,
}
@staticmethod
def generate_key():
key_obj = StringIO()
key = RSAKey.generate(2048)
key.write_private_key(key_obj)
return key_obj.getvalue(), 'ssh-rsa ' + key.get_base64()
def add_public_key(self, public_key):
command = f'mkdir -p -m 700 ~/.ssh && \
echo {public_key!r} >> ~/.ssh/authorized_keys && \
chmod 600 ~/.ssh/authorized_keys'
2019-11-24 09:05:58 +00:00
code, out = self.exec_command(command)
2019-11-11 01:16:18 +00:00
if code != 0:
2019-11-24 09:05:58 +00:00
raise Exception(out)
2019-11-11 01:16:18 +00:00
def ping(self):
with self:
return True
2019-12-12 03:39:35 +00:00
def get_client(self):
if self.client is not None:
return self.client
self.client = SSHClient()
self.client.set_missing_host_key_policy(AutoAddPolicy)
self.client.connect(**self.arguments)
return self.client
2019-11-24 09:05:58 +00:00
def exec_command(self, command, timeout=1800, environment=None):
2019-11-11 01:16:18 +00:00
with self as cli:
2019-11-24 09:05:58 +00:00
chan = cli.get_transport().open_session()
chan.settimeout(timeout)
chan.set_combine_stderr(True)
if environment:
chan.update_environment(environment)
chan.exec_command(command)
out = chan.makefile("r", -1)
return chan.recv_exit_status(), out.read()
2019-11-11 01:16:18 +00:00
2019-11-24 09:05:58 +00:00
def exec_command_with_stream(self, command, timeout=1800, environment=None):
2019-11-11 01:16:18 +00:00
with self as cli:
2019-11-24 09:05:58 +00:00
chan = cli.get_transport().open_session()
chan.settimeout(timeout)
chan.set_combine_stderr(True)
if environment:
chan.update_environment(environment)
chan.exec_command(command)
stdout = chan.makefile("r", -1)
out = stdout.readline()
while out:
yield chan.exit_status, out
out = stdout.readline()
return chan.exit_status, out
2019-11-11 01:16:18 +00:00
def __enter__(self):
if self.client is not None:
raise RuntimeError('Already connected')
2019-12-12 03:39:35 +00:00
return self.get_client()
2019-11-11 01:16:18 +00:00
def __exit__(self, exc_type, exc_val, exc_tb):
self.client.close()
self.client = None