<commit_before>"""
Create a SCP side-channel to transfer a file to remote network device.

SCP requires a separate SSH connection.

Currently only supports Cisco IOS and Cisco ASA.
"""

from __future__ import print_function
from __future__ import unicode_literals

import re
import os
import hashlib

import paramiko
import scp


class SCPConn(object):
    """Establish a secure copy channel to the remote network device."""
    def __init__(self, ssh_conn):
        self.ssh_ctl_chan = ssh_conn
        self.establish_scp_conn()

    def establish_scp_conn(self):
        """Establish the secure copy connection."""
        self.scp_conn = paramiko.SSHClient()
        self.scp_conn.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        self.scp_conn.connect(hostname=self.ssh_ctl_chan.ip,
                              port=self.ssh_ctl_chan.port,
                              username=self.ssh_ctl_chan.username,
                              password=self.ssh_ctl_chan.password,
                              look_for_keys=False,
                              allow_agent=False,
                              timeout=8)
        self.scp_client = scp.SCPClient(self.scp_conn.get_transport())

    def scp_transfer_file(self, source_file, dest_file):
        """
        Transfer file using SCP

        Must close the SCP connection to get the file to write to the remote filesystem
        """
        self.scp_client.put(source_file, dest_file)

    def close(self):
        """Close the SCP connection."""
        self.scp_conn.close()


class FileTransfer(object):
    """Class to manage SCP file transfer and associated SSH control channel."""
    def __init__(self, ssh_conn, source_file, dest_file, file_system=None):
        self.ssh_ctl_chan = ssh_conn
        self.source_file = source_file
        self.source_md5 = self.file_md5(source_file)
        self.dest_file = dest_file
        src_file_stats = os.stat(source_file)
        self.file_size = src_file_stats.st_size

        check_file_systems = ['flash:', 'bootflash:']
        if not file_system:
            for test_fs in check_file_systems:
                cmd = "dir {}".format(test_fs)
                output = self.ssh_ctl_chan.send_command_expect(cmd)
                if '% Invalid' not in output:
                    file_system = test_fs
                    break
            else:
                raise ValueError("Standard file_system not found on remote device for SCP. " \
                                 "Please specify file_system")
        else:
            self.file_system = file_system

    def __enter__(self):
        """Context manager setup"""
        self.establish_scp_conn()
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        """Context manager cleanup."""
        self.close_scp_chan()
        if exc_type is not None:
            raise exc_type(exc_value)

    def establish_scp_conn(self):
        """Establish SCP connection."""
        self.scp_conn = SCPConn(self.ssh_ctl_chan)

    def close_scp_chan(self):
        """Close the SCP connection to the remote network device."""
        self.scp_conn.close()
        self.scp_conn = None

    def verify_space_available(self, search_pattern=r"bytes total \((.*) bytes free\)"):
        """Verify sufficient space is available on remote network device (return boolean)."""
        remote_cmd = "dir {0}".format(self.file_system)
        remote_output = self.ssh_ctl_chan.send_command_expect(remote_cmd)
        match = re.search(search_pattern, remote_output)
        space_avail = int(match.group(1))
        if space_avail > self.file_size:
            return True
        return False
<commit_msg>Improving autodetection of file system for SCP<commit_after>"""
Create a SCP side-channel to transfer a file to remote network device.

SCP requires a separate SSH connection.

Currently only supports Cisco IOS and Cisco ASA.
"""

from __future__ import print_function
from __future__ import unicode_literals

import re
import os
import hashlib

import paramiko
import scp


class SCPConn(object):
    """Establish a secure copy channel to the remote network device."""
    def __init__(self, ssh_conn):
        self.ssh_ctl_chan = ssh_conn
        self.establish_scp_conn()

    def establish_scp_conn(self):
        """Establish the secure copy connection."""
        self.scp_conn = paramiko.SSHClient()
        self.scp_conn.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        self.scp_conn.connect(hostname=self.ssh_ctl_chan.ip,
                              port=self.ssh_ctl_chan.port,
                              username=self.ssh_ctl_chan.username,
                              password=self.ssh_ctl_chan.password,
                              look_for_keys=False,
                              allow_agent=False,
                              timeout=8)
        self.scp_client = scp.SCPClient(self.scp_conn.get_transport())

    def scp_transfer_file(self, source_file, dest_file):
        """
        Transfer file using SCP

        Must close the SCP connection to get the file to write to the remote filesystem
        """
        self.scp_client.put(source_file, dest_file)

    def close(self):
        """Close the SCP connection."""
        self.scp_conn.close()


class FileTransfer(object):
    """Class to manage SCP file transfer and associated SSH control channel."""
    def __init__(self, ssh_conn, source_file, dest_file, file_system=None):
        self.ssh_ctl_chan = ssh_conn
        self.source_file = source_file
        self.source_md5 = self.file_md5(source_file)
        self.dest_file = dest_file
        src_file_stats = os.stat(source_file)
        self.file_size = src_file_stats.st_size
        if not file_system:
            self._autodetect_fs()
        else:
            self.file_system = file_system

    def __enter__(self):
        """Context manager setup"""
        self.establish_scp_conn()
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        """Context manager cleanup."""
        self.close_scp_chan()
        if exc_type is not None:
            raise exc_type(exc_value)

    def _autodetect_fs(self):
        """
        Autodetect the file system on the remote device.

        Should work for Cisco IOS, IOS-XE, and Cisco ASA.
        """
        cmd = "dir"
        output = self.ssh_ctl_chan.send_command_expect(cmd)
        match = re.search(r'Directory of (.*)/', output)
        if match:
            self.file_system = match.group(1)
            # Test file_system
            cmd = "dir {}".format(self.file_system)
            output = self.ssh_ctl_chan.send_command_expect(cmd)
            if '% Invalid' not in output:
                return None

        raise ValueError("An error occurred in dynamically determining remote file " \
                         "system: {} {}".format(cmd, output))

    def establish_scp_conn(self):
        """Establish SCP connection."""
        self.scp_conn = SCPConn(self.ssh_ctl_chan)

    def close_scp_chan(self):
        """Close the SCP connection to the remote network device."""
        self.scp_conn.close()
        self.scp_conn = None

    def verify_space_available(self, search_pattern=r"bytes total \((.*) bytes free\)"):
        """Verify sufficient space is available on remote network device (return boolean)."""
        remote_cmd = "dir {0}".format(self.file_system)
        remote_output = self.ssh_ctl_chan.send_command_expect(remote_cmd)
        match = re.search(search_pattern, remote_output)
        space_avail = int(match.group(1))
        if space_avail > self.file_size:
            return True
        return False

{'fraction_non_alphanumeric': 0.0858145884800416, 'fraction_numerical': 0.0014302431413340268, 'mean_word_length': 2.2928082191780823, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4083198', 'n_tokens_mistral': 2261, 'n_tokens_neox': 2184, 'n_words': 558}