ffdc: Implement for use with Telnet protocol

- Set 1: First pass for comments and feedback.
- Set 2: Adjust timeout/waittime calculation

Test:
- Set 1: Test telnet protocol
- Set 2: Test telnet protocol. Regression test other protocols.

Signed-off-by: Peter D  Phan <peterp@us.ibm.com>
Change-Id: Ifbf317fa8dbe3b5a8ba5cbea1fbd4bf4f6c6ba63
diff --git a/ffdc/ffdc_collector.py b/ffdc/ffdc_collector.py
index 3ef0072..121cb74 100644
--- a/ffdc/ffdc_collector.py
+++ b/ffdc/ffdc_collector.py
@@ -12,6 +12,7 @@
 from errno import EACCES, EPERM
 import subprocess
 from ssh_utility import SSHRemoteclient
+from telnet_utility import TelnetRemoteclient
 
 
 class FFDCCollector:
@@ -47,7 +48,8 @@
             self.password = password
             self.ffdc_config = ffdc_config
             self.location = location
-            self.remote_client = None
+            self.ssh_remoteclient = None
+            self.telnet_remoteclient = None
             self.ffdc_dir_path = ""
             self.ffdc_prefix = ""
             self.target_type = remote_type.upper()
@@ -129,12 +131,17 @@
             else:
                 print("\n\t[Check] %s Redfish Service.\t\t [NOT AVAILABLE]" % self.hostname)
 
+            # IPMI
             if self.verify_ipmi():
                 working_protocol_list.append("IPMI")
                 print("\n\t[Check] %s IPMI LAN Service.\t\t [OK]" % self.hostname)
             else:
                 print("\n\t[Check] %s IPMI LAN Service.\t\t [NOT AVAILABLE]" % self.hostname)
 
+            # Telnet
+            if self.telnet_to_target_system():
+                working_protocol_list.append("TELNET")
+
             # Verify top level directory exists for storage
             self.validate_local_store(self.location)
             print("\n\t---- Completed protocol pre-requisite check ----\n")
@@ -154,18 +161,35 @@
 
         """
 
-        self.remoteclient = SSHRemoteclient(self.hostname,
-                                            self.username,
-                                            self.password)
+        self.ssh_remoteclient = SSHRemoteclient(self.hostname,
+                                                self.username,
+                                                self.password)
 
-        self.remoteclient.ssh_remoteclient_login()
-        print("\n\t[Check] %s SSH connection established.\t [OK]" % self.hostname)
+        if self.ssh_remoteclient.ssh_remoteclient_login():
+            print("\n\t[Check] %s SSH connection established.\t [OK]" % self.hostname)
 
-        # Check scp connection.
-        # If scp connection fails,
-        # continue with FFDC generation but skip scp files to local host.
-        self.remoteclient.scp_connection()
-        return True
+            # Check scp connection.
+            # If scp connection fails,
+            # continue with FFDC generation but skip scp files to local host.
+            self.ssh_remoteclient.scp_connection()
+            return True
+        else:
+            print("\n\t[Check] %s SSH connection.\t [NOT AVAILABLE]" % self.hostname)
+            return False
+
+    def telnet_to_target_system(self):
+        r"""
+        Open a telnet connection to targeted system.
+        """
+        self.telnet_remoteclient = TelnetRemoteclient(self.hostname,
+                                                      self.username,
+                                                      self.password)
+        if self.telnet_remoteclient.tn_remoteclient_login():
+            print("\n\t[Check] %s Telnet connection established.\t [OK]" % self.hostname)
+            return True
+        else:
+            print("\n\t[Check] %s Telnet connection.\t [NOT AVAILABLE]" % self.hostname)
+            return False
 
     def generate_ffdc(self, working_protocol_list):
         r"""
@@ -205,6 +229,12 @@
                     else:
                         print("\n\tERROR: SSH or SCP is not available for %s." % self.hostname)
 
+                if ffdc_actions[machine_type][k]['PROTOCOL'][0] == 'TELNET':
+                    if 'TELNET' in working_protocol_list:
+                        self.protocol_telnet(ffdc_actions, machine_type, k)
+                    else:
+                        print("\n\tERROR: TELNET is not available for %s." % self.hostname)
+
                 if ffdc_actions[machine_type][k]['PROTOCOL'][0] == 'REDFISH':
                     if 'REDFISH' in working_protocol_list:
                         self.protocol_redfish(ffdc_actions, machine_type, k)
@@ -219,7 +249,8 @@
 
         # Close network connection after collecting all files
         self.elapsed_time = time.strftime("%H:%M:%S", time.gmtime(time.time() - self.start_time))
-        self.remoteclient.ssh_remoteclient_disconnect()
+        self.ssh_remoteclient.ssh_remoteclient_disconnect()
+        self.telnet_remoteclient.tn_remoteclient_disconnect()
 
     def protocol_ssh(self,
                      ffdc_actions,
@@ -239,6 +270,44 @@
         else:
             self.collect_and_copy_ffdc(ffdc_actions[machine_type][sub_type])
 
+    def protocol_telnet(self,
+                        ffdc_actions,
+                        machine_type,
+                        sub_type):
+        r"""
+        Perform actions using telnet protocol.
+        Description of argument(s):
+        ffdc_actions        List of actions from ffdc_config.yaml.
+        machine_type        OS Type of remote host.
+        """
+        print("\n\t[Run] Executing commands on %s using %s" % (self.hostname, 'TELNET'))
+        telnet_files_saved = []
+        progress_counter = 0
+        list_of_commands = ffdc_actions[machine_type][sub_type]['COMMANDS']
+        for index, each_cmd in enumerate(list_of_commands, start=0):
+            command_txt, command_timeout = self.unpack_command(each_cmd)
+            result = self.telnet_remoteclient.execute_command(command_txt, command_timeout)
+            if result:
+                try:
+                    targ_file = ffdc_actions[machine_type][sub_type]['FILES'][index]
+                except IndexError:
+                    targ_file = each_cmd
+                    print("\n\t[WARN] Missing filename to store data from telnet %s." % each_cmd)
+                    print("\t[WARN] Data will be stored in %s." % targ_file)
+                targ_file_with_path = (self.ffdc_dir_path
+                                       + self.ffdc_prefix
+                                       + targ_file)
+                # Creates a new file
+                with open(targ_file_with_path, 'wb') as fp:
+                    fp.write(result)
+                    fp.close
+                    telnet_files_saved.append(targ_file)
+            progress_counter += 1
+            self.print_progress(progress_counter)
+        print("\n\t[Run] Commands execution completed.\t\t [OK]")
+        for file in telnet_files_saved:
+            print("\n\t\tSuccessfully save file " + file + ".")
+
     def protocol_redfish(self,
                          ffdc_actions,
                          machine_type,
@@ -351,7 +420,7 @@
                                        form_filename)
 
         # Copying files
-        if self.remoteclient.scpclient:
+        if self.ssh_remoteclient.scpclient:
             print("\n\n\tCopying FFDC files from remote system %s.\n" % self.hostname)
 
             # Retrieving files from target system
@@ -388,6 +457,24 @@
             list_of_files = []
         return list_of_files
 
+    def unpack_command(self,
+                       command):
+        r"""
+        Unpack command from config file
+
+        Description of argument(s):
+        command    Command from config file.
+        """
+        if isinstance(command, dict):
+            command_txt = next(iter(command))
+            command_timeout = next(iter(command.values()))
+        elif isinstance(command, str):
+            command_txt = command
+            # Default command timeout 60 seconds
+            command_timeout = 60
+
+        return command_txt, command_timeout
+
     def ssh_execute_ffdc_commands(self,
                                   ffdc_actions_for_machine_type,
                                   form_filename=False):
@@ -408,18 +495,12 @@
 
         progress_counter = 0
         for command in list_of_commands:
-            if isinstance(command, dict):
-                command_txt = next(iter(command))
-                command_timeout = next(iter(command.values()))
-            elif isinstance(command, str):
-                command_txt = command
-                # Default ssh command timeout 60 seconds
-                command_timeout = 60
+            command_txt, command_timeout = self.unpack_command(command)
 
             if form_filename:
                 command_txt = str(command_txt % self.target_type)
 
-            err, response = self.remoteclient.execute_command(command_txt, command_timeout)
+            err, response = self.ssh_remoteclient.execute_command(command_txt, command_timeout)
 
             progress_counter += 1
             self.print_progress(progress_counter)
@@ -435,7 +516,7 @@
         ffdc_actions_for_machine_type    commands and files for the selected remote host type.
         """
 
-        if self.remoteclient.scpclient:
+        if self.ssh_remoteclient.scpclient:
             print("\n\tCopying DUMP files from remote system %s.\n" % self.hostname)
 
             list_of_commands = self.get_command_list(ffdc_actions_for_machine_type)
@@ -450,10 +531,10 @@
                     print("\t\tInvalid command %s for DUMP_LOGS block." % command)
                     continue
 
-                err, response = self.remoteclient.execute_command(command)
+                err, response = self.ssh_remoteclient.execute_command(command)
 
                 if response:
-                    scp_result = self.remoteclient.scp_file_from_remote(filename, self.ffdc_dir_path)
+                    scp_result = self.ssh_remoteclient.scp_file_from_remote(filename, self.ffdc_dir_path)
                     if scp_result:
                         print("\t\tSuccessfully copied from " + self.hostname + ':' + filename)
                 else:
@@ -488,9 +569,9 @@
 
             # If source file name contains wild card, copy filename as is.
             if '*' in source_file_path:
-                scp_result = self.remoteclient.scp_file_from_remote(source_file_path, self.ffdc_dir_path)
+                scp_result = self.ssh_remoteclient.scp_file_from_remote(source_file_path, self.ffdc_dir_path)
             else:
-                scp_result = self.remoteclient.scp_file_from_remote(source_file_path, targ_file_path)
+                scp_result = self.ssh_remoteclient.scp_file_from_remote(source_file_path, targ_file_path)
 
             if not quiet:
                 if scp_result:
diff --git a/ffdc/ssh_utility.py b/ffdc/ssh_utility.py
index 56d4dad..9715948 100644
--- a/ffdc/ssh_utility.py
+++ b/ffdc/ssh_utility.py
@@ -7,7 +7,6 @@
 from paramiko.ssh_exception import BadHostKeyException
 from paramiko.buffered_pipe import PipeTimeout as PipeTimeout
 from scp import SCPClient, SCPException
-import sys
 import time
 import socket
 from socket import timeout as SocketTimeout
@@ -43,6 +42,7 @@
         Method to create a ssh connection to remote host.
         """
 
+        is_ssh_login = True
         try:
             # SSHClient to make connections to the remote server
             self.sshclient = paramiko.SSHClient()
@@ -56,8 +56,9 @@
 
         except (BadHostKeyException, AuthenticationException,
                 SSHException, NoValidConnectionsError, socket.error) as e:
-            print("\n>>>>>\tERROR: Unable to SSH to %s %s %s\n\n" % (self.hostname, e.__class__, e))
-            sys.exit(-1)
+            is_ssh_login = False
+
+        return is_ssh_login
 
     def ssh_remoteclient_disconnect(self):
 
diff --git a/ffdc/telnet_utility.py b/ffdc/telnet_utility.py
index 29698eb..8635bf0 100644
--- a/ffdc/telnet_utility.py
+++ b/ffdc/telnet_utility.py
@@ -1,5 +1,7 @@
 #!/usr/bin/env python
 
+
+import time
 import socket
 import telnetlib
 from collections import deque
@@ -73,8 +75,6 @@
             pass
 
     def execute_command(self, cmd,
-                        rtnpartial=False,
-                        wait_cnt=5,
                         i_timeout=300):
 
         r'''
@@ -91,44 +91,21 @@
         '''
 
         # Execute the command and read the command output
+        # Execute the command and read the command output
+        return_buffer = b''
         try:
 
             # Flush whatever data is in the read buffer by doing
             # a non-blocking read
-            self.tnclient.read_very_eager()
+            self.tnclient.read_very_eager().decode('utf-8')
 
             # Execute the command
-            self.tnclient.write(cmd.encode('utf-8') + b"\n")
+            self.tnclient.write(cmd.encode('utf-8') + b'\n')
+            time.sleep(i_timeout)
 
-            # Read the command output.  Read until we get command prompt
-            l_buf = ''
-            l_xcnt = 0
-            while(True):
-                index, match, b = self.tnclient.expect([br'\$', br'\#'], i_timeout)
+            # Read the command output.
+            return_buffer = self.tnclient.read_very_eager()
 
-                if(b == ''):
-                    # Nothing read.  Increment the counter & retry.
-                    l_xcnt = l_xcnt + 1
-                    if(l_xcnt >= wait_cnt):
-                        l_time_waited = str((l_xcnt * i_timeout) / 60)
-                        print("\t\t ERROR Timeout execute Telnet command")
-                        break
-                else:
-                    # We got some data.  Reset the counter.
-                    l_xcnt = 0
-                    l_buf = l_buf + b.decode('utf-8').strip()
-
-                if (index != -1) and (match is not None):
-                    # We got the command prompt. Have read the complete command
-                    # output.
-                    break
-
-                if rtnpartial:
-                    print("\t\t WARN "
-                          + "Have not read the command prompt. "
-                          + "Returning command output read.")
-
-                    return l_buf
         except (socket.error, EOFError) as e:
             self.tn_remoteclient_disconnect()
 
@@ -137,11 +114,8 @@
             elif str(e).__contains__("telnet connection closed"):
                 msg = "Telnet connection closed."
             else:
-                msg = "Some other issue. Connection got reset!!"
+                msg = "Some other issue.%s %s %s\n\n" % (cmd, e.__class__, e)
 
             print("\t\t ERROR %s " % msg)
-            return ''
 
-        # Remove command prompt
-        c = l_buf[0: (len(l_buf) - 1)]
-        return c
+        return return_buffer