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