Initial base infrastructure support for open BMC FFDC

This change includes the following
   - initial support struture for FFDC
   - scp connection introduced

Resolves openbmc/openbmc-test-automation#39

Change-Id: Id0656401b1f4b73d4b2500e70c9ba78f11bd7879
Signed-off-by: George Keishing <gkeishin@in.ibm.com>
diff --git a/.gitignore b/.gitignore
index 8d80b3a..1cdf9a4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@
 /.tox/
 /tools/argument_file.txt
 /logsdir/
+/logs
diff --git a/lib/connection_client.robot b/lib/connection_client.robot
index b67996c..e3bbdff 100755
--- a/lib/connection_client.robot
+++ b/lib/connection_client.robot
@@ -53,6 +53,14 @@
 
     Login  ${username}  ${password}
 
+Open Connection for SCP
+    Import Library      SCPLibrary      WITH NAME       scp
+    Run Keyword If  '${SSH_PORT}' == '${EMPTY}'  scp.Open connection  ${OPENBMC_HOST}
+    ...  username=${OPENBMC_USERNAME}  password=${OPENBMC_PASSWORD}
+    ...  ELSE   Run Keyword    scp.Open connection  ${OPENBMC_HOST}  port=${SSH_PORT}
+    ...  username=${OPENBMC_USERNAME}  password=${OPENBMC_PASSWORD}
+
+
 User input SSH and HTTPs Ports
     [Documentation]   Update the global SSH and HTTPs port variable for QEMU
     ${port_num}=    Convert To Integer    ${SSH_PORT}
diff --git a/lib/openbmc_ffdc.robot b/lib/openbmc_ffdc.robot
new file mode 100644
index 0000000..eb0bd71
--- /dev/null
+++ b/lib/openbmc_ffdc.robot
@@ -0,0 +1,156 @@
+*** Settings ***
+Documentation      This module is for collecting data on test case failure
+...                for openbmc systems. It will collect the data with a
+...                default name openbmc_ffdc_report.txt under directory
+...                logs/testSuite/testcaseName/ on failure.
+
+Library            String
+Library            DateTime
+Library            openbmc_ffdc_list.py
+
+Resource           resource.txt
+Resource           connection_client.robot
+
+*** Variables ***
+
+${PRINT_LINE}      ------------------------------------------------------------------------
+
+${MSG_INTRO}       This document contains the following information:
+${MSG_DETAIL}      ${\n}\t\t[ Detailed Logs Captured Section ]
+${HEADER_MSG}      ${\n}${PRINT_LINE}${\n}\t\tOPEN BMC TEST FAILURE DATA CAPTURE
+...                ${\n}\t\t----------------------------------
+...                ${\n}${\n}TEST SUITE FILE\t\t: ${SUITE_NAME} ${\n}
+${FOOTER_MSG}      ${PRINT_LINE} ${\n}
+
+${FFDC_LOG_PATH}   ${EXECDIR}${/}logs${/}
+
+*** Keywords ***
+
+Log FFDC
+    [Documentation]   Generic FFDC entry point. Place holder to hook in
+    ...               other data collection
+
+    Log FFDC If Test Case Failed
+
+
+Log FFDC If Test Case Failed
+    [Documentation]   Main entry point to gather logs on Test case failure
+
+    # Return from here if the test case is a PASS
+    Return From Keyword If  '${TEST_STATUS}' != 'FAIL'
+
+    ${cur_time}=       get current time stamp
+    Log To Console     ${\n}FFDC Collection Started \t: ${cur_time}
+    # Log directory setup
+    ${suite_dir}=      get strip string   ${SUITE_NAME}
+    ${testname_dir}=   get strip string   ${TEST_NAME}
+
+    Set Suite Variable   ${FFDC_DIR_PATH}   ${FFDC_LOG_PATH}${suite_dir}${/}${testname_dir}
+
+    # -- FFDC workspace create --
+    create ffdc directory
+    openbmc header message
+
+    # -- FFDC processing entry point --
+    Execute FFDC command list on BMC
+
+    ${cur_time}=       get current time stamp
+    Log To Console     FFDC Collection Completed \t: ${cur_time}
+    Log                ${\n}${FFDC_DIR_PATH}
+
+
+create ffdc directory
+    [Documentation]    Creates directory and report file
+    Create Directory   ${FFDC_DIR_PATH}
+    create ffdc report file
+
+
+create ffdc report file
+    [Documentation]     Create a generic file name for ffdc
+    Set Suite Variable  ${FFDC_FILE_PATH}   ${FFDC_DIR_PATH}${/}openbmc_ffdc_report.txt
+    Create File         ${FFDC_FILE_PATH}
+
+
+write data to file
+    [Documentation]     Write data to the ffdc report document
+    [Arguments]         ${data}=""
+    Append To File      ${FFDC_FILE_PATH}   ${data}
+
+
+get current time stamp
+    [Documentation]     Get the current time stamp data
+    ${cur_time}=    Get Current Date      result_format=%Y-%m-%d %H:%M:%S,%f
+    [return]   ${cur_time}
+
+openbmc header message
+    [Documentation]     Write header message to the report document
+    ${cur_time}=    get current time stamp
+    write data to file    ${HEADER_MSG}
+    write data to file    TEST CASE NAME\t\t: ${TEST_NAME}${\n}
+    write data to file    FAILURE TIME STAMP\t: ${cur_time}${\n}
+    write data to file    ${\n}${MSG_INTRO}${\n}
+
+    # --- FFDC header notes ---
+    @{entries}=     Get ffdc index
+    :FOR  ${index}  IN   @{entries}
+    \   write data to file   * ${index.upper()}
+    \   write data to file   ${\n}
+
+    write data to file    ${FOOTER_MSG}
+    write data to file    ${MSG_DETAIL}
+
+write cmd output to ffdc file
+    [Documentation]     Write cmd output data to the report document
+    [Arguments]         ${data_str}=""   ${data_cmd}=""
+    write data to file  ${\n}${FOOTER_MSG}
+    write data to file  ${ENTRY_CMD_TYPE.upper()} : ${data_str}\t
+    write data to file  Executed : ${data_cmd} ${\n}
+    write data to file  ${FOOTER_MSG}
+
+
+Execute FFDC command list on BMC
+    [Documentation]    Get the commands, connect to BMC and execute commands
+    ${con_status}=   Run Keyword And Return Status    Open Connection And Log In
+    Run Keyword And Return If   ${con_status} == ${False}  Log  Open Connection Failed
+
+    @{entries}=     Get ffdc index
+    :FOR  ${index}  IN   @{entries}
+    \     Loop through ffdc dict list and execute   ${index}
+
+
+Loop through ffdc dict list and execute
+    [Documentation]    Feed in key pair list from dictionary to execute
+    [Arguments]        ${data_str}=
+    @{ffdc_default_list}=    Get ffdc cmd    ${data_str}
+
+    Set Suite Variable   ${ENTRY_CMD_TYPE}   ${data_str}
+    :FOR  ${cmd}  IN  @{ffdc_default_list}
+    \    Execute command and write to ffdc    ${cmd[0]}  ${cmd[1]}
+
+
+Execute command and write to ffdc
+    [Documentation]    Execute command on bmc box and write to ffdc
+    [Arguments]        ${data_str}=""   ${data_cmd}=""
+    write cmd output to ffdc file   ${data_str}  ${data_cmd}
+
+    ${stdout}  ${stderr}=    Execute Command    ${data_cmd}   return_stderr=True
+    # Write stdout data on success and error msg to the file on failure
+    Run Keyword If   '${stderr}' == '${EMPTY}'   write data to file   ${stdout} ${\n}
+    ...  ELSE  Run Keyword   write data to file  ${stderr} ${\n}
+    write data to file    ${FOOTER_MSG}
+
+
+Offload file list from BMC
+    [Documentation]    Copy files to current log directory
+    ${con_status}=     Run Keyword And Return Status    Open Connection for SCP
+    Run Keyword And Return If   ${con_status} == ${False}  Log  SCP Connection Failed
+
+    # --- Files to be copied ---
+    @{ffdc_default_list}=    Get ffdc file    BMC Files
+    Set Suite Variable   ${ENTRY_CMD_TYPE}    BMC Files
+
+    :FOR  ${cmd}  IN  @{ffdc_default_list}
+    # Get File from server to current test FFDC directory
+    \    write cmd output to ffdc file  ${cmd[0]}  scp file ${cmd[1]}
+    \    scp.Get File    ${cmd[1]}  ${FFDC_DIR_PATH}
+
diff --git a/lib/openbmc_ffdc_list.py b/lib/openbmc_ffdc_list.py
new file mode 100644
index 0000000..3a2950a
--- /dev/null
+++ b/lib/openbmc_ffdc_list.py
@@ -0,0 +1,84 @@
+#!/usr/bin/python
+'''
+#############################################################
+#    @file     openbmc_ffdc_list.py
+#    @author:  George Keishing
+#
+#    @brief    List for FFDC ( First failure data capture )
+#              commands and files to be collected as a part
+#              of the test case failure.
+#############################################################
+'''
+
+#-------------------
+# FFDC default list
+#-------------------
+
+#-----------------------------------------------------------------
+#Dict Name {  Index string : { Key String :  Comand string} }
+#-----------------------------------------------------------------
+FFDC_CMD = {
+             'DRIVER INFO' :
+                     {
+                        'FW Level' : 'cat /etc/os-release',
+                        'OS Details' : 'uname -a',
+                        'Build Info' : 'cat /etc/version',
+                     },
+             'BMC DATA' :
+                     {
+                        'System journal log' : 'journalctl --no-pager',
+                        'Displays processor activity' : 'top -n 1 -b',
+                     },
+             'APPLICATION DATA' :
+                     {
+                        'BMC state' : '/usr/sbin/obmcutil  state',
+                     },
+           }
+
+# add file list needed to be offload from BMC
+FFDC_FILE = {
+             'BMC FILES' :
+                     {
+                        # Sample example how to add the file that
+                        # is needed to be offloaded
+                        #'Release info' : '/etc/os-release',
+                     },
+           }
+
+#-----------------------------------------------------------------
+
+
+# base class for FFDC default list
+class openbmc_ffdc_list():
+
+    ########################################################################
+    #   @@brief   This method returns the list from the dictionary for cmds
+    #   @param    i_type: @type string: string index lookup
+    #   @return   List of key pair from the dictionary
+    ########################################################################
+    def get_ffdc_cmd(self,i_type):
+        return FFDC_CMD[i_type].items()
+
+    ########################################################################
+    #   @@brief   This method returns the list from the dictionary for scp
+    #   @param    i_type: @type string: string index lookup
+    #   @return   List of key pair from the dictionary
+    ########################################################################
+    def get_ffdc_file(self,i_type):
+        return FFDC_FILE[i_type].items()
+
+    ########################################################################
+    #   @@brief   This method returns the list index from dictionary
+    #   @return   List of index to the dictionary
+    ########################################################################
+    def get_ffdc_index(self):
+        return FFDC_CMD.keys()
+
+    ########################################################################
+    #   @brief    Returns the stripped strings
+    #   @param    i_str: @type string: string name
+    #   @return   Remove all special chars and return the string
+    ########################################################################
+    def get_strip_string(self, i_str):
+        return ''.join(e for e in i_str if e.isalnum())
+