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                # Need to do read/write operation to trigger
94                # paramiko exec_command timeout mechanism.
95                xresults = stderr.readlines()
96                results = ''.join(xresults)
97                time.sleep(1)
98                if stdout.channel.exit_status_ready():
99                    break
100            cmd_exit_code = stdout.channel.recv_exit_status()
101
102            # Convert list of string to one string
103            err = ''
104            out = ''
105            for item in results:
106                err += item
107            for item in stdout.readlines():
108                out += item
109
110            return cmd_exit_code, err, out
111
112        except (paramiko.AuthenticationException, paramiko.SSHException,
113                paramiko.ChannelException, SocketTimeout) as e:
114            # Log command with error. Return to caller for next command, if any.
115            logging.error("\n\tERROR: Fail remote command %s %s" % (e.__class__, e))
116            logging.error("\tCommand '%s' Elapsed Time %s" %
117                          (command, time.strftime("%H:%M:%S", time.gmtime(time.time() - cmd_start))))
118            return 0, empty, empty
119
120    def scp_connection(self):
121
122        r"""
123        Create a scp connection for file transfer.
124        """
125        try:
126            self.scpclient = SCPClient(self.sshclient.get_transport(), sanitize=lambda x: x)
127            logging.info("\n\t[Check] %s SCP transport established.\t [OK]" % self.hostname)
128        except (SCPException, SocketTimeout, PipeTimeout) as e:
129            self.scpclient = None
130            logging.error("\n\tERROR: SCP get_transport has failed. %s %s" % (e.__class__, e))
131            logging.info("\tScript continues generating FFDC on %s." % self.hostname)
132            logging.info("\tCollected data will need to be manually offloaded.")
133
134    def scp_file_from_remote(self, remote_file, local_file):
135
136        r"""
137        scp file in remote system to local with date-prefixed filename.
138
139        Description of argument(s):
140        remote_file            Full path filename on the remote host
141
142        local_file             Full path filename on the local host
143                               local filename = date-time_remote filename
144
145        """
146
147        try:
148            self.scpclient.get(remote_file, local_file, recursive=True)
149        except (SCPException, SocketTimeout, PipeTimeout) as e:
150            # Log command with error. Return to caller for next file, if any.
151            logging.error(
152                "\n\tERROR: Fail scp %s from remotehost %s %s\n\n" % (remote_file, e.__class__, e))
153            return False
154
155        # Return True for file accounting
156        return True
157