def get(self, remotepaths, localpath=''): """ Copies one or more files from the remote host to the local host. """ remotepaths = self._make_list(remotepaths) localpath = localpath or os.getcwd() globs = [] noglobs = [] for rpath in remotepaths: if glob.has_magic(rpath): globs.append(rpath) else: noglobs.append(rpath) globresults = [self.glob(g) for g in globs] remotepaths = noglobs for globresult in globresults: remotepaths.extend(globresult) recursive = False for rpath in remotepaths: if not self.path_exists(rpath): raise exception.BaseException( "Remote file or directory does not exist: %s" % rpath) for rpath in remotepaths: if self.isdir(rpath): recursive = True break try: self.scp.get(remotepaths, local_path=localpath, recursive=recursive) except Exception as e: log.debug("get failed: remotepaths=%s, localpath=%s", str(remotepaths), localpath) raise exception.SCPException(str(e))
def scp(self): """Initialize the SCP client.""" if not self._scp or not self._scp.transport.is_active(): log.debug("creating scp connection") self._scp = scp.SCPClient(self.transport, progress=self._file_transfer_progress, socket_timeout=self._timeout) return self._scp
def switch_user(self, user): """ Reconnect, if necessary, to host as user """ if not self.is_active() or user and self.get_current_user() != user: self.connect(username=user) else: user = user or self._username log.debug("already connected as user %s" % user)
def _load_rsa_key(self, private_key, private_key_pass=None): private_key_file = os.path.expanduser(private_key) try: rsa_key = get_rsa_key(key_location=private_key_file, passphrase=private_key_pass) log.debug("Using private key %s (RSA)" % private_key) return rsa_key except (paramiko.SSHException, exception.SSHError): log.error('invalid rsa key or passphrase specified')
def remove_lines_from_file(self, remote_file, regex): """ Removes lines matching regex from remote_file """ if regex in [None, '']: log.debug('no regex supplied...returning') return lines = self.get_remote_file_lines(remote_file, regex, matching=False) log.debug("new %s after removing regex (%s) matches:\n%s" % (remote_file, regex, ''.join(lines))) f = self.remote_file(remote_file) f.writelines(lines) f.close()
def connect(self, host=None, username=None, password=None, private_key=None, private_key_pass=None, port=None, timeout=30, compress=None): host = host or self._host username = username or self._username password = password or self._password compress = compress or self._compress port = port if port is not None else self._port pkey = self._pkey if private_key: pkey = self.load_private_key(private_key, private_key_pass) log.debug("connecting to host %s on port %d as user %s" % (host, port, username)) try: sock = self._get_socket(host, port) transport = paramiko.Transport(sock) transport.banner_timeout = timeout except socket.error: raise exception.SSHConnectionError(host, port) # Enable/disable compression transport.use_compression(compress) # Authenticate the transport. try: transport.connect(username=username, pkey=pkey, password=password) except paramiko.AuthenticationException: raise exception.SSHAuthException(username, host) except paramiko.SSHException as e: msg = e.args[0] raise exception.SSHError(msg) except socket.error: raise exception.SSHConnectionError(host, port) except EOFError: raise exception.SSHConnectionError(host, port) except Exception as e: raise exception.SSHError(str(e)) self.close() self._transport = transport try: assert self.sftp is not None except paramiko.SFTPError as e: if 'Garbage packet received' in e: log.debug("Garbage packet received", exc_info=True) raise exception.SSHAccessDeniedViaAuthKeys(username) raise return self
def load_private_key(self, private_key, private_key_pass=None): # Use Private Key. log.debug('loading private key %s' % private_key) if private_key.endswith('rsa') or private_key.count('rsa'): pkey = self._load_rsa_key(private_key, private_key_pass) elif private_key.endswith('dsa') or private_key.count('dsa'): pkey = self._load_dsa_key(private_key, private_key_pass) else: log.debug( "specified key does not end in either rsa or dsa, trying both") pkey = self._load_rsa_key(private_key, private_key_pass) if pkey is None: pkey = self._load_dsa_key(private_key, private_key_pass) return pkey
def get_private_rsa_fingerprint(key_location=None, key_file_obj=None, passphrase=None): """ Returns the fingerprint of a private RSA key as a 59-character string (40 characters separated every 2 characters by a ':'). The fingerprint is computed using the SHA1 (hex) digest of the DER-encoded (pkcs8) RSA private key. """ k = get_rsa_key(key_location=key_location, key_file_obj=key_file_obj, passphrase=passphrase, use_pycrypto=True) sha1digest = hashlib.sha1(k.exportKey('DER', pkcs=8)).hexdigest() fingerprint = insert_char_every_n_chars(sha1digest, ':', 2) key = key_location or key_file_obj log.debug("rsa private key fingerprint (%s): %s" % (key, fingerprint)) return fingerprint
def put(self, localpaths, remotepath='.'): """ Copies one or more files from the local host to the remote host. """ localpaths = self._make_list(localpaths) recursive = False for lpath in localpaths: if os.path.isdir(lpath): recursive = True break try: self.scp.put(localpaths, remote_path=remotepath, recursive=recursive) except Exception as e: log.debug("put failed: localpaths=%s, remotepath=%s", str(localpaths), remotepath) raise exception.SCPException(str(e))
def get_public_rsa_fingerprint(key_location=None, key_file_obj=None, passphrase=None): """ Returns the fingerprint of the public portion of an RSA key as a 47-character string (32 characters separated every 2 characters by a ':'). The fingerprint is computed using the MD5 (hex) digest of the DER-encoded RSA public key. """ privkey = get_rsa_key(key_location=key_location, key_file_obj=key_file_obj, passphrase=passphrase, use_pycrypto=True) pubkey = privkey.publickey() md5digest = hashlib.md5(pubkey.exportKey('DER')).hexdigest() fingerprint = insert_char_every_n_chars(md5digest, ':', 2) key = key_location or key_file_obj log.debug("rsa public key fingerprint (%s): %s" % (key, fingerprint)) return fingerprint
def __del__(self): """Attempt to clean up if not explicitly closed.""" log.debug('__del__ called') self.close()
def execute(self, command, silent=True, only_printable=False, ignore_exit_status=False, log_output=True, detach=False, source_profile=True, raise_on_failure=True): """ Execute a remote command and return stdout/stderr NOTE: this function blocks until the process finishes kwargs: silent - don't print the command's output to the console only_printable - filter the command's output to allow only printable characters ignore_exit_status - don't warn about non-zero exit status log_output - log all remote output to the debug file detach - detach the remote process so that it continues to run even after the SSH connection closes (does NOT return output or check for non-zero exit status if detach=True) source_profile - if True prefix the command with "source /etc/profile" raise_on_failure - raise exception.SSHError if command fails returns List of output lines """ channel = self.transport.open_session() if detach: command = "nohup %s &" % command if source_profile: command = "source /etc/profile && %s" % command channel.exec_command(command) channel.close() self.__last_status = None return if source_profile: command = "source /etc/profile && %s" % command log.debug("executing remote command: %s" % command) channel.exec_command(command) output = self._get_output(channel, silent=silent, only_printable=only_printable) exit_status = channel.recv_exit_status() self.__last_status = exit_status out_str = b'\n'.join(output) if exit_status != 0: msg = "remote command '%s' failed with status %d" msg %= (command, exit_status) if log_output: msg += ":\n%s" % out_str else: msg += " (no output log requested)" if not ignore_exit_status: if raise_on_failure: raise exception.RemoteCommandFailed( msg, command, exit_status, out_str) else: log.error(msg) else: log.debug("(ignored) " + msg) else: if log_output: log.debug("output of '%s':\n%s" % (command, out_str)) else: log.debug("output of '%s' has been hidden" % command) return output
def sftp(self): """Establish the SFTP connection.""" if not self._sftp or self._sftp.sock.closed: log.debug("creating sftp connection") self._sftp = paramiko.SFTPClient.from_transport(self.transport) return self._sftp