ffdc: Add code to handle OEM OSes

- Set 1: Initial code to handle OEM OSes.
- Set 2: Modify commit message.
- Set 3: Flag error when user input OS type is not described in config file.

Test:
- Set 1: Regressing tests to OpenBmc, RHEL, Ubuntu, AIX, OEM

Signed-off-by: Peter D  Phan <peterp@us.ibm.com>
Change-Id: Id5410d21c7e4f8f5e74f1dded212b935075f7bea
diff --git a/ffdc/collect_ffdc.py b/ffdc/collect_ffdc.py
index 1915b2a..1153739 100644
--- a/ffdc/collect_ffdc.py
+++ b/ffdc/collect_ffdc.py
@@ -32,8 +32,7 @@
               show_default=True, help="YAML Configuration file listing commands and files for FFDC.")
 @click.option('-l', '--location', default="/tmp",
               show_default=True, help="Location to store collected FFDC data")
-@click.option('-t', '--remote_type', default="OPENBMC",
-              show_default=True,
+@click.option('-t', '--remote_type',
               help="OS type of the remote (targeting) host. OPENBMC, RHEL, UBUNTU, AIX")
 @click.option('-rp', '--remote_protocol', default="ALL",
               show_default=True,
@@ -46,7 +45,7 @@
 
     click.echo("\n********** FFDC (First Failure Data Collection) Starts **********")
 
-    if input_options_ok(remote, username, password, ffdc_config):
+    if input_options_ok(remote, username, password, ffdc_config, remote_type):
         thisFFDC = FFDCCollector(remote, username, password,
                                  ffdc_config, location, remote_type, remote_protocol)
         thisFFDC.collect_ffdc()
@@ -62,7 +61,7 @@
     click.echo("\n********** FFDC Finishes **********\n\n")
 
 
-def input_options_ok(remote, username, password, ffdc_config):
+def input_options_ok(remote, username, password, ffdc_config, remote_type):
     r"""
     Verify script options exist via CLI options or environment variables.
     """
@@ -82,6 +81,10 @@
         print("\
         \n>>>>>\tERROR: Password for user on remote host is not specified in CLI options "
               + "or env OPENBMC_PASSWORD.")
+    if not remote_type:
+        all_options_ok = False
+        print("\
+        \n>>>>>\tERROR: Remote host os type is not specified in CLI options.")
     if not os.path.isfile(ffdc_config):
         all_options_ok = False
         print("\
diff --git a/ffdc/ffdc_collector.py b/ffdc/ffdc_collector.py
index 2604650..3657136 100644
--- a/ffdc/ffdc_collector.py
+++ b/ffdc/ffdc_collector.py
@@ -61,8 +61,9 @@
         with open(self.ffdc_config, 'r') as file:
             self.ffdc_actions = yaml.load(file, Loader=yaml.FullLoader)
 
-        # List of supported OSes.
-        self.supported_oses = self.ffdc_actions['DISTRO_OS'][0]
+        if self.target_type not in self.ffdc_actions.keys():
+            print("\n\tERROR: %s is not listed in %s.\n\n" % (self.target_type, self.ffdc_config))
+            sys.exit(-1)
 
     def verify_script_env(self):
 
@@ -106,69 +107,6 @@
             print("\n>>>>>\tERROR: %s is not ping-able. FFDC collection aborted.\n" % self.hostname)
             sys.exit(-1)
 
-    def inspect_target_machine_type(self):
-        r"""
-        Inspect remote host os-release or uname.
-
-        """
-        print("\n\tSupported distro: %s" % self.supported_oses)
-
-        command = "cat /etc/os-release"
-        response = self.remoteclient.execute_command(command)
-        if response:
-            print("\n\t[INFO] %s /etc/os-release\n" % self.hostname)
-            for each_info in response:
-                print("\t\t %s" % each_info)
-            identity = self.find_os_type(response, 'ID').split('=')[1].upper()
-        else:
-            response = self.remoteclient.execute_command('uname -a')
-            print("\n\t[INFO] %s uname -a\n" % self.hostname)
-            print("\t\t %s" % ' '.join(response))
-            identity = self.find_os_type(response, 'AIX').split(' ')[0].upper()
-
-            # If OS does not have /etc/os-release and is not AIX,
-            # script does not yet know what to do.
-            if not identity:
-                print(">>>>>\tERROR: Script does not yet know about %s" % ' '.join(response))
-                sys.exit(-1)
-
-        if (self.target_type not in identity):
-
-            user_target_type = self.target_type
-            self.target_type = ""
-            for each_os in self.supported_oses:
-                if each_os in identity:
-                    self.target_type = each_os
-                    break
-
-            # If OS in not one of ['OPENBMC', 'RHEL', 'AIX', 'UBUNTU']
-            # script does not yet know what to do.
-            if not self.target_type:
-                print(">>>>>\tERROR: Script does not yet know about %s" % identity)
-                sys.exit(-1)
-
-            print("\n\t[WARN] user request %s does not match remote host type %s.\n"
-                  % (user_target_type, self.target_type))
-            print("\t[WARN] FFDC collection continues for %s.\n" % self.target_type)
-
-    def find_os_type(self,
-                     listing_from_os,
-                     key):
-        r"""
-        Return OS information with the requested key
-
-        Description of argument(s):
-
-        listing_from_os    list of information returns from OS command
-        key                key of the desired data
-
-        """
-
-        for each_item in listing_from_os:
-            if key in each_item:
-                return each_item
-        return ''
-
     def collect_ffdc(self):
         r"""
         Initiate FFDC Collection depending on requested protocol.
@@ -189,17 +127,16 @@
                 working_protocol_list.append("REDFISH")
                 print("\n\t[Check] %s Redfish Service.\t\t [OK]" % self.hostname)
             else:
-                print("\n\t[Check] %s Redfish Service.\t\t [FAILED]" % self.hostname)
+                print("\n\t[Check] %s Redfish Service.\t\t [NOT AVAILABLE]" % self.hostname)
 
             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 [FAILED]" % self.hostname)
+                print("\n\t[Check] %s IPMI LAN Service.\t\t [NOT AVAILABLE]" % self.hostname)
 
             # Verify top level directory exists for storage
             self.validate_local_store(self.location)
-            self.inspect_target_machine_type()
             print("\n\t---- Completed protocol pre-requisite check ----\n")
 
             if ((self.remote_protocol not in working_protocol_list) and (self.remote_protocol != 'ALL')):
@@ -261,13 +198,22 @@
                     continue
 
                 if ffdc_actions[machine_type][k]['PROTOCOL'][0] == 'SSH':
-                    self.protocol_ssh(ffdc_actions, machine_type, k)
+                    if 'SSH' in working_protocol_list:
+                        self.protocol_ssh(ffdc_actions, machine_type, k)
+                    else:
+                        print("\n\tERROR: SSH is not available for %s." % self.hostname)
 
                 if ffdc_actions[machine_type][k]['PROTOCOL'][0] == 'REDFISH':
-                    self.protocol_redfish(ffdc_actions, machine_type, k)
+                    if 'REDFISH' in working_protocol_list:
+                        self.protocol_redfish(ffdc_actions, machine_type, k)
+                    else:
+                        print("\n\tERROR: REDFISH is not available for %s." % self.hostname)
 
                 if ffdc_actions[machine_type][k]['PROTOCOL'][0] == 'IPMI':
-                    self.protocol_ipmi(ffdc_actions, machine_type, k)
+                    if 'IPMI' in working_protocol_list:
+                        self.protocol_ipmi(ffdc_actions, machine_type, k)
+                    else:
+                        print("\n\tERROR: IMPI is not available for %s." % self.hostname)
 
         # Close network connection after collecting all files
         self.elapsed_time = time.strftime("%H:%M:%S", time.gmtime(time.time() - self.start_time))
@@ -398,19 +344,11 @@
         form_filename                    if true, pre-pend self.target_type to filename
         """
 
-        print("\n\t[Run] Executing commands on %s using %s"
-              % (self.hostname, ffdc_actions_for_machine_type['PROTOCOL'][0]))
-        list_of_commands = ffdc_actions_for_machine_type['COMMANDS']
-        progress_counter = 0
-        for command in list_of_commands:
-            if form_filename:
-                command = str(command % self.target_type)
-            self.remoteclient.execute_command(command)
-            progress_counter += 1
-            self.print_progress(progress_counter)
+        # Executing commands, , if any
+        self.ssh_execute_ffdc_commands(ffdc_actions_for_machine_type,
+                                       form_filename)
 
-        print("\n\t[Run] Commands execution completed.\t\t [OK]")
-
+        # Copying files
         if self.remoteclient.scpclient:
             print("\n\n\tCopying FFDC files from remote system %s.\n" % self.hostname)
 
@@ -420,6 +358,43 @@
         else:
             print("\n\n\tSkip copying FFDC files from remote system %s.\n" % self.hostname)
 
+    def ssh_execute_ffdc_commands(self,
+                                  ffdc_actions_for_machine_type,
+                                  form_filename=False):
+        r"""
+        Send commands in ffdc_config file to targeted system.
+
+        Description of argument(s):
+        ffdc_actions_for_machine_type    commands and files for the selected remote host type.
+        form_filename                    if true, pre-pend self.target_type to filename
+        """
+        print("\n\t[Run] Executing commands on %s using %s"
+              % (self.hostname, ffdc_actions_for_machine_type['PROTOCOL'][0]))
+        list_of_commands = ffdc_actions_for_machine_type['COMMANDS']
+
+        # If command list is empty, returns
+        if not list_of_commands:
+            return
+
+        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
+
+            if form_filename:
+                command_txt = str(command_txt % self.target_type)
+
+            self.remoteclient.execute_command(command_txt, command_timeout)
+            progress_counter += 1
+            self.print_progress(progress_counter)
+
+        print("\n\t[Run] Commands execution completed.\t\t [OK]")
+
     def group_copy(self,
                    ffdc_actions_for_machine_type):
         r"""
@@ -428,6 +403,10 @@
         Description of argument(s):
         ffdc_actions_for_machine_type    commands and files for the selected remote host type.
         """
+
+        # Executing commands, if any
+        self.ssh_execute_ffdc_commands(ffdc_actions_for_machine_type)
+
         if self.remoteclient.scpclient:
             print("\n\tCopying DUMP files from remote system %s.\n" % self.hostname)
 
diff --git a/ffdc/ssh_utility.py b/ffdc/ssh_utility.py
index 2110b74..44a76ff 100644
--- a/ffdc/ssh_utility.py
+++ b/ffdc/ssh_utility.py
@@ -70,7 +70,7 @@
         if self.scpclient:
             self.scpclient.close()
 
-    def execute_command(self, command):
+    def execute_command(self, command, default_timeout=60):
         """
         Execute command on the remote host.
 
@@ -80,7 +80,7 @@
         """
 
         try:
-            stdin, stdout, stderr = self.sshclient.exec_command(command)
+            stdin, stdout, stderr = self.sshclient.exec_command(command, timeout=default_timeout)
             stdout.channel.recv_exit_status()
             response = stdout.readlines()
             return response