1#!/usr/bin/env python
2
3import paramiko
4from paramiko.ssh_exception import AuthenticationException
5from paramiko.ssh_exception import NoValidConnectionsError
6from paramiko.ssh_exception import SSHException
7from paramiko.ssh_exception import BadHostKeyException
8from paramiko.buffered_pipe import PipeTimeout as PipeTimeout
9from scp import SCPClient, SCPException
10import time
11import socket
12import logging
13from socket import timeout as SocketTimeout
14
15
16class SSHRemoteclient:
17    r"""
18    Class to create ssh connection to remote host
19    for remote host command execution and scp.
20    """
21
22    def __init__(self, hostname, username, password):
23
24        r"""
25        Description of argument(s):
26
27        hostname        Name/IP of the remote (targeting) host
28        username        User on the remote host with access to FFCD files
29        password        Password for user on remote host
30        """
31
32        self.ssh_output = None
33        self.ssh_error = None
34        self.sshclient = None
35        self.scpclient = None
36        self.hostname = hostname
37        self.username = username
38        self.password = password
39
40    def ssh_remoteclient_login(self):
41
42        r"""
43        Method to create a ssh connection to remote host.
44        """
45
46        is_ssh_login = True
47        try:
48            # SSHClient to make connections to the remote server
49            self.sshclient = paramiko.SSHClient()
50            # setting set_missing_host_key_policy() to allow any host
51            self.sshclient.set_missing_host_key_policy(paramiko.AutoAddPolicy())
52            # Connect to the server
53            self.sshclient.connect(hostname=self.hostname,
54                                   username=self.username,
55                                   password=self.password,
56                                   look_for_keys=False)
57
58        except (BadHostKeyException, AuthenticationException,
59                SSHException, NoValidConnectionsError, socket.error) as e:
60            is_ssh_login = False
61
62        return is_ssh_login
63
64    def ssh_remoteclient_disconnect(self):
65
66        r"""
67        Clean up.
68        """
69
70        if self.sshclient:
71            self.sshclient.close()
72
73        if self.scpclient:
74            self.scpclient.close()
75
76    def execute_command(self, command,
77                        default_timeout=60):
78        """
79        Execute command on the remote host.
80
81        Description of argument(s):
82        command                Command string sent to remote host
83
84        """
85
86        empty = ''
87        cmd_start = time.time()
88        try:
89            stdin, stdout, stderr = \
90                self.sshclient.exec_command(command, timeout=default_timeout)
91            start = time.time()
92            while time.time() < start + default_timeout:
93                if stdout.channel.exit_status_ready():
94                    break
95                time.sleep(1)
96            cmd_exit_code = stdout.channel.recv_exit_status()
97
98            # Convert list of string to one string
99            err = ''
100            out = ''
101            for item in stderr.readlines():
102                err += item
103            for item in stdout.readlines():
104                out += item
105
106            return cmd_exit_code, err, out
107
108        except (paramiko.AuthenticationException, paramiko.SSHException,
109                paramiko.ChannelException, SocketTimeout) as e:
110            # Log command with error. Return to caller for next command, if any.
111            logging.error("\n\tERROR: Fail remote command %s %s" % (e.__class__, e))
112            logging.error("\tCommand '%s' Elapsed Time %s" %
113                          (command, time.strftime("%H:%M:%S", time.gmtime(time.time() - cmd_start))))
114            return 0, empty, empty
115
116    def scp_connection(self):
117
118        r"""
119        Create a scp connection for file transfer.
120        """
121        try:
122            self.scpclient = SCPClient(self.sshclient.get_transport(), sanitize=lambda x: x)
123            logging.info("\n\t[Check] %s SCP transport established.\t [OK]" % self.hostname)
124        except (SCPException, SocketTimeout, PipeTimeout) as e:
125            self.scpclient = None
126            logging.error("\n\tERROR: SCP get_transport has failed. %s %s" % (e.__class__, e))
127            logging.info("\tScript continues generating FFDC on %s." % self.hostname)
128            logging.info("\tCollected data will need to be manually offloaded.")
129
130    def scp_file_from_remote(self, remote_file, local_file):
131
132        r"""
133        scp file in remote system to local with date-prefixed filename.
134
135        Description of argument(s):
136        remote_file            Full path filename on the remote host
137
138        local_file             Full path filename on the local host
139                               local filename = date-time_remote filename
140
141        """
142
143        try:
144            self.scpclient.get(remote_file, local_file, recursive=True)
145        except (SCPException, SocketTimeout, PipeTimeout) as e:
146            # Log command with error. Return to caller for next file, if any.
147            logging.error(
148                "\n\tERROR: Fail scp %s from remotehost %s %s\n\n" % (remote_file, e.__class__, e))
149            return False
150
151        # Return True for file accounting
152        return True
153