blob: f10651a251bd8c1b468d9ef015702313dbef61dc [file] [log] [blame]
from time import sleep
import platform
import subprocess
import os
import socket
import select
import sys
import termios
import tty
import paramiko
from paramiko.ssh_exception import AuthenticationException, SSHException
import discoverer
import config
import console
import keys
class KeyPushError(Exception):
pass
class DefaultLoginError(Exception):
pass
class SshClient:
def __init__(self, device, address):
self.config = config.Config()
self.keystore = keys.Keystore()
self.device = device
self.address = address
self.username = self.config.username()
self.password = self.config.password()
self.ssh_command = self.config.sshCommand()
if not self.maybeGenerateSshKeys():
return False
self.client = paramiko.SSHClient()
self.client.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
def _shouldPushKey(self):
try:
self.client.connect(
self.address,
username=self.username,
pkey=self.keystore.key(),
allow_agent=False,
look_for_keys=False,
compress=True)
except AuthenticationException as e:
return True
except (SSHException, socket.error) as e:
raise e
finally:
self.client.close()
def _pushKey(self):
try:
self.client.connect(
self.address,
username=self.username,
password=self.password,
allow_agent=False,
look_for_keys=False,
compress=True)
except AuthenticationException as e:
raise DefaultLoginError(e)
except (SSHException, socket.error) as e:
raise KeyPushError(e)
else:
public_key = self.keystore.key().get_base64()
self.client.exec_command('mkdir -p $HOME/.ssh')
self.client.exec_command(
'echo ssh-rsa {0} mdt@localhost >>$HOME/.ssh/authorized_keys'.format(public_key))
finally:
self.client.close()
def maybeGenerateSshKeys(self):
if not self.keystore.key():
print('Looks like you don\'t have a private key yet. Generating one.')
if not self.keystore.generateKey():
print('Unable to generate private key.')
return False
return True
def openShell(self):
term = os.getenv("TERM", default="vt100")
width, height = os.get_terminal_size()
if self._shouldPushKey():
print("Key not present on {0} -- pushing".format(self.device))
self._pushKey()
self.client.connect(
self.address,
username=self.username,
pkey=self.keystore.key(),
allow_agent=False,
look_for_keys=False,
compress=True)
return self.client.invoke_shell(term=term, width=width, height=height)
def close(self):
self.client.close()
class Shell:
def __init__(self):
self.config = config.Config()
self.discoverer = discoverer.Discoverer(self)
self.device = self.config.preferredDevice()
self.address = None
def add_device(self, hostname, address):
if not self.device:
self.device = hostname
self.address = address
elif self.device == hostname:
self.address = address
def run(self, args):
if len(args) > 1:
self.device = args[1]
if not self.address:
if self.device:
print('Waiting for device {0}...'.format(self.device))
else:
print('Waiting for a device...')
while not self.address:
sleep(0.1)
print('Connecting to {0} at {1}'.format(self.device, self.address))
try:
client = SshClient(self.device, self.address)
channel = client.openShell()
cons = console.Console(channel, sys.stdin)
cons.run()
except KeyPushError as e:
print("Unable to push keys to the device: {0}".format(e))
return 1
except DefaultLoginError as e:
print("Can't login using default credentials: {0}".format(e))
return 1
except SSHException as e:
print("Couldn't establish ssh connection to device: {0}".format(e))
return 1
except socket.error as e:
print("Couldn't establish ssh connection to device: {0}".format(e))
return 1
except console.SocketTimeoutError as e:
print("Connection to {0} at {1} closed: socket timeout".format(self.device, self.address))
return 1
except console.ConnectionClosedError as e:
print("Connection to {0} at {1} closed".format(self.device, self.address))
return 0
finally:
client.close()