ffdc: Integrate ffdc script into robot framework flow.

This commit provides interface to enable automation tests (robot/jenkins)
to activate the new cli ffdc collector.

- Patchset 1: Initial commit for review.
- Patchset 2: Redesing and fixing issues
- Patchset 3: Fix build issue (pycodestyle)
- Patchset 4: Default ffdc store location running direct CLI
- Patchset 5: Addressing review comments and questions
- Patchset 6: Restore direct CLI default location to /tmp
- Patchset 7: Addressing issues from comments
              Also extend socket timeout for long-running command peltool -a
- Patchset 8: Use Builtin().log_to_console("") method in the bridge code
- Patchset 9: Rebase

Tests:
- Standalone script: python3 ./ffdc/collect_ffdc.py
- Within robot env: python3 -m robot .... -v FFDC_DEFAULT:0 ./tools/myffdc.robot

Signed-off-by: Peter D  Phan <peterp@us.ibm.com>
Change-Id: Ic892c48efe7dbdb3a4345ba89c3a0257f03a7ffb
diff --git a/ffdc/collect_ffdc.py b/ffdc/collect_ffdc.py
index 7711fd6..c1c9495 100644
--- a/ffdc/collect_ffdc.py
+++ b/ffdc/collect_ffdc.py
@@ -18,7 +18,7 @@
     for found_dir in dirs:
         sys.path.append(os.path.join(root, found_dir))
 
-from ffdc_collector import FFDCCollector
+from ffdc_collector import ffdc_collector
 
 
 @click.command(context_settings=dict(help_option_names=['-h', '--help']))
@@ -61,26 +61,26 @@
     click.echo("\n********** FFDC (First Failure Data Collection) Starts **********")
 
     if input_options_ok(remote, username, password, config, type):
-        thisFFDC = FFDCCollector(remote,
-                                 username,
-                                 password,
-                                 config,
-                                 location,
-                                 type,
-                                 protocol,
-                                 env_vars,
-                                 econfig,
-                                 log_level)
-        thisFFDC.collect_ffdc()
+        this_ffdc = ffdc_collector(remote,
+                                   username,
+                                   password,
+                                   config,
+                                   location,
+                                   type,
+                                   protocol,
+                                   env_vars,
+                                   econfig,
+                                   log_level)
+        this_ffdc.collect_ffdc()
 
-        if len(os.listdir(thisFFDC.ffdc_dir_path)) == 0:
+        if len(os.listdir(this_ffdc.ffdc_dir_path)) == 0:
             click.echo("\n\tFFDC Collection from " + remote + " has failed.\n\n")
         else:
-            click.echo(str("\n\t" + str(len(os.listdir(thisFFDC.ffdc_dir_path)))
+            click.echo(str("\n\t" + str(len(os.listdir(this_ffdc.ffdc_dir_path)))
                            + " files were retrieved from " + remote))
-            click.echo("\tFiles are stored in " + thisFFDC.ffdc_dir_path)
+            click.echo("\tFiles are stored in " + this_ffdc.ffdc_dir_path)
 
-        click.echo("\tTotal elapsed time " + thisFFDC.elapsed_time + "\n\n")
+        click.echo("\tTotal elapsed time " + this_ffdc.elapsed_time + "\n\n")
     click.echo("\n********** FFDC Finishes **********\n\n")
 
 
diff --git a/ffdc/ffdc_collector.py b/ffdc/ffdc_collector.py
index 4a8cfa4..61590a3 100644
--- a/ffdc/ffdc_collector.py
+++ b/ffdc/ffdc_collector.py
@@ -14,6 +14,8 @@
 import platform
 from errno import EACCES, EPERM
 import subprocess
+
+sys.path.extend([f'./{name[0]}' for name in os.walk(".") if os.path.isdir(name[0])])
 from ssh_utility import SSHRemoteclient
 from telnet_utility import TelnetRemoteclient
 
@@ -33,8 +35,8 @@
        - arg1
        - arg2
 """
-abs_path = os.path.abspath(os.path.dirname(sys.argv[0]))
-plugin_dir = abs_path + '/plugins'
+plugin_dir = __file__.split(__file__.split("/")[-1])[0] + '/plugins'
+sys.path.append(plugin_dir)
 try:
     for module in os.listdir(plugin_dir):
         if module == '__init__.py' or module[-3:] != '.py':
@@ -98,7 +100,7 @@
 }
 
 
-class FFDCCollector:
+class ffdc_collector:
 
     r"""
     Execute commands from configuration file to collect log files.
@@ -154,9 +156,9 @@
         # to be sure that all files for this run will have same timestamps
         # and they will be saved in the same directory.
         # self.location == local system for now
-        self.set_ffdc_defaults()
+        self.set_ffdc_default_store_path()
 
-        # Logger for this run.  Need to be after set_ffdc_defaults()
+        # Logger for this run.  Need to be after set_ffdc_default_store_path()
         self.script_logging(getattr(logging, log_level.upper()))
 
         # Verify top level directory exists for storage
@@ -181,7 +183,7 @@
         # Load ENV vars from user.
         self.logger.info("\n\tENV: User define input YAML variables")
         self.env_dict = {}
-        self. load_env()
+        self.load_env()
 
     def verify_script_env(self):
 
@@ -686,7 +688,7 @@
                 progress_counter += 1
                 self.print_progress(progress_counter)
 
-    def set_ffdc_defaults(self):
+    def set_ffdc_default_store_path(self):
         r"""
         Set a default value for self.ffdc_dir_path and self.ffdc_prefix.
         Collected ffdc file will be stored in dir /self.location/hostname_timestr/.
@@ -704,7 +706,11 @@
         self.ffdc_prefix = timestr + "_"
         self.validate_local_store(self.ffdc_dir_path)
 
-    def validate_local_store(self, dir_path):
+    # Need to verify local store path exists prior to instantiate this class.
+    # This class method is used to share the same code between CLI input parm
+    # and Robot Framework "${EXECDIR}/logs" before referencing this class.
+    @classmethod
+    def validate_local_store(cls, dir_path):
         r"""
         Ensure path exists to store FFDC files locally.
 
@@ -916,7 +922,7 @@
 
     def execute_plugin_block(self, plugin_cmd_list):
         r"""
-        Pack the plugin command to quailifed python string object.
+        Pack the plugin command to qualifed python string object.
 
         Description of argument(s):
         plugin_list_dict      Plugin block read from YAML
diff --git a/ffdc/ffdc_config.yaml b/ffdc/ffdc_config.yaml
index 1f7e041..b2bd403 100644
--- a/ffdc/ffdc_config.yaml
+++ b/ffdc/ffdc_config.yaml
@@ -61,7 +61,7 @@
             - 'cat /var/log/obmc-console.log >/tmp/BMC_obmc_console.txt'
             - 'cat /var/log/obmc-console1.log >/tmp/BMC_obmc_console1.txt'
             - 'peltool -l >/tmp/PEL_logs_list.json'
-            - 'peltool -a >/tmp/PEL_logs_display.json'
+            - {'peltool -a >/tmp/PEL_logs_display.json':240}
             - 'hexdump -C /var/lib/phosphor-logging/extensions/pels/badPEL >/tmp/PEL_logs_badPEL.txt'
             - 'guard -l >/tmp/GUARD_list.txt'
             - 'killall -s SIGUSR1 pldmd; sleep 5'
diff --git a/ffdc/plugins/redfish.py b/ffdc/plugins/redfish.py
index 33a0669..0ea7a00 100644
--- a/ffdc/plugins/redfish.py
+++ b/ffdc/plugins/redfish.py
@@ -82,7 +82,7 @@
             response = execute_redfish_cmd(parms + resource)
             # Enumeration is done for available resources ignoring the
             # ones for which response is not obtained.
-            if 'Response Error' in response:
+            if 'Error getting response' in response:
                 continue
 
             walk_nested_dict(response, url=resource)