Tests for Downloading Image onto the BMC

- Upload images via REST
- Upload images via TFTP

Resolves openbmc/openbmc-test-automation#510

Change-Id: Id55f12d0d0354024c57ca929953b408e70876b40
Signed-off-by: Saqib Khan <khansa@us.ibm.com>
diff --git a/data/variables.py b/data/variables.py
index 8d86e6f..759bc6c 100644
--- a/data/variables.py
+++ b/data/variables.py
@@ -43,6 +43,9 @@
 # Software manager version
 SOFTWARE_VERSION_URI = '/xyz/openbmc_project/software/'
 ACTIVE = 'xyz.openbmc_project.Software.Activation.Activations.Active'
+READY = 'xyz.openbmc_project.Software.Activation.Activations.Ready'
+INVALID = 'xyz.openbmc_project.Software.Activation.Activations.Invalid'
+ACTIVATING = 'xyz.openbmc_project.Software.Activation.Activations.Activating'
 
 # Inventory URI
 HOST_INVENTORY_URI = '/xyz/openbmc_project/inventory/'
diff --git a/extended/test_uploadimage.py b/extended/test_uploadimage.py
new file mode 100644
index 0000000..44a0963
--- /dev/null
+++ b/extended/test_uploadimage.py
@@ -0,0 +1,173 @@
+#!/usr/bin/env python
+
+r"""
+This module is the python counterpart to test_uploadimage.robot.
+"""
+
+import os
+import sys
+import re
+import string
+import tarfile
+import time
+
+robot_pgm_dir_path = os.path.dirname(__file__) + os.sep
+repo_lib_path = re.sub('/extended/', '/lib', robot_pgm_dir_path)
+repo_data_path = re.sub('/extended/', '/data', robot_pgm_dir_path)
+sys.path.append(repo_lib_path)
+sys.path.append(repo_data_path)
+
+import gen_robot_keyword as grk
+import gen_print as gp
+import variables as var
+from robot.libraries.BuiltIn import BuiltIn
+
+
+###############################################################################
+def get_latest_file(dir_path):
+
+    r"""
+    Get the path to the latest uploaded file.
+
+    Description of argument(s):
+    dir_path    Path to the dir from which the name of the last
+                updated file or folder will be returned to the
+                calling function.
+    """
+
+    grk.run_key_u("Open Connection And Log In")
+    status, ret_values =\
+            grk.run_key("Execute Command On BMC  cd " + dir_path
+            + "; stat -c '%Y %n' * | sort -k1,1nr | head -n 1", ignore=1)
+    return ret_values.split(" ")[-1]
+
+###############################################################################
+
+
+###############################################################################
+def get_version_tar(tar_file_path):
+
+    r"""
+    Read the image version from the MANIFEST inside the tarball.
+
+    Description of argument(s):
+    tar_file_path    The path to a tar file that holds the image
+                     version inside the MANIFEST.
+    """
+
+    tar = tarfile.open(tar_file_path)
+    for member in tar.getmembers():
+        f=tar.extractfile(member)
+        content=f.read()
+        if "version=" in content:
+            content = content.split("\n")
+            content = [x for x in content if "version=" in x]
+            version = content[0].split("=")[-1]
+            break
+    tar.close()
+    return version
+
+###############################################################################
+
+
+###############################################################################
+def get_image_version(file_path):
+
+    r"""
+    Read the file for a version object.
+
+    Description of argument(s):
+    file_path    The path to a file that holds the image version.
+    """
+
+    grk.run_key_u("Open Connection And Log In")
+    status, ret_values =\
+            grk.run_key("Execute Command On BMC  cat "
+            + file_path + " | grep \"version=\"")
+    return ret_values.split("=")[-1]
+
+###############################################################################
+
+
+###############################################################################
+def get_image_purpose(file_path):
+
+    r"""
+    Read the file for a purpose object.
+
+    Description of argument(s):
+    file_path    The path to a file that holds the image purpose.
+    """
+
+    grk.run_key_u("Open Connection And Log In")
+    status, ret_values =\
+            grk.run_key("Execute Command On BMC  cat "
+            + file_path + " | grep \"purpose=\"")
+    return ret_values.split("=")[-1]
+
+###############################################################################
+
+
+###############################################################################
+def get_image_path(image_version):
+
+    r"""
+    Query the upload image dir for the presence of image matching
+    the version that was read from the MANIFEST before uploading
+    the image. Based on the purpose verify the activation object
+    exists and is either READY or INVALID.
+
+    Description of argument(s):
+    image_version    The version of the image that should match one
+                     of the images in the upload dir.
+    """
+
+    upload_dir = BuiltIn().get_variable_value("${UPLOAD_DIR_PATH}")
+    grk.run_key_u("Open Connection And Log In")
+    status, image_list =\
+            grk.run_key("Execute Command On BMC  ls -d " + upload_dir
+            + "*/")
+
+    image_list = image_list.split("\n")
+    retry = 0
+    while (retry < 10):
+        for i in range(0, len(image_list)):
+            version = get_image_version(image_list[i] + "MANIFEST")
+            if (version == image_version):
+                return image_list[i]
+        time.sleep(10)
+        retry += 1
+
+###############################################################################
+
+
+###############################################################################
+def verify_image_upload():
+
+    r"""
+    Verify the image was uploaded correctly and that it created
+    a valid d-bus object
+    """
+
+    image_version = BuiltIn().get_variable_value("${IMAGE_VERSION}")
+    image_path = get_image_path(image_version)
+    image_version_id = image_path.split("/")[-2]
+
+    grk.run_key_u("Open Connection And Log In")
+    image_purpose = get_image_purpose(image_path + "MANIFEST")
+    if (image_purpose == "bmc" or image_purpose == "host"):
+        uri = var.SOFTWARE_VERSION_URI + "/" + image_version_id
+        status, ret_values =\
+        grk.run_key("Read Attribute  " + uri + "  Activation")
+
+        if ((ret_values == var.READY) or (ret_values == var.INVALID)
+            or (ret_values == var.ACTIVE)):
+            return True
+        else:
+            gp.print_var(ret_values)
+            return False
+    else:
+        gp.print_var(versionPurpose)
+        return False
+
+###############################################################################
diff --git a/extended/test_uploadimage.robot b/extended/test_uploadimage.robot
new file mode 100644
index 0000000..19dc35a
--- /dev/null
+++ b/extended/test_uploadimage.robot
@@ -0,0 +1,81 @@
+*** Settings ***
+Documentation         Test Upload Image
+...                   Execution Method :
+...                   python -m robot -v OPENBMC_HOST:<hostname>
+...                   -v TFTP_SERVER:<TFTP server IP>
+...                   -v TFTP_FILE_NAME:<filename.tar>
+...                   -v IMAGE_FILE_PATH:<path/*.tar> test_uploadimage.robot
+
+Resource              ../lib/connection_client.robot
+Resource              ../lib/rest_client.robot
+Resource              ../lib/openbmc_ffdc.robot
+Library               Collections
+Library               String
+Library               OperatingSystem
+Library               test_uploadimage.py
+
+Test Teardown  Upload Image Teardown
+
+*** Variables ***
+${timeout}            10
+${UPLOAD_DIR_PATH}    /tmp/images/
+${QUIET}              ${1}
+${IMAGE_VERSION}      ${EMPTY}
+
+*** Test Cases ***
+
+Upload Image Via REST
+    [Documentation]  Upload an image via REST.
+    [Tags]  Upload_Image_Via_REST
+
+    OperatingSystem.File Should Exist  ${IMAGE_FILE_PATH}
+    ${IMAGE_VERSION}=  Get Version Tar  ${IMAGE_FILE_PATH}
+    ${image_data}=  OperatingSystem.Get Binary File  ${IMAGE_FILE_PATH}
+    Upload Post Request  /upload/image  data=${image_data}
+    ${ret}=  Verify Image Upload
+    Should Be True  True == ${ret}
+
+Upload Image Via TFTP
+    [Documentation]  Upload an image via TFTP.
+    [Tags]  Upload_Image_Via_TFTP
+
+    @{image}=  Create List  ${TFTP_FILE_NAME}  ${TFTP_SERVER}
+    ${data}=  Create Dictionary  data=@{image}
+    ${resp}=  OpenBMC Post Request
+    ...  ${SOFTWARE_VERSION_URI}/action/DownloadViaTFTP  data=${data}
+    Should Be Equal As Strings  ${resp.status_code}  ${HTTP_OK}
+    Sleep  1 minute
+    ${upload_file}=  Get Latest File  ${UPLOAD_DIR_PATH}
+    ${IMAGE_VERSION}=  Get Image Version
+    ...  ${UPLOAD_DIR_PATH}${upload_file}/MANIFEST
+    ${ret}=  Verify Image Upload
+    Should Be True  True == ${ret}
+
+*** Keywords ***
+
+Upload Image Teardown
+    [Documentation]  Log FFDC if test suite fails and collect SOL log for
+    ...              debugging purposes.
+
+    Close All Connections
+    FFDC On Test Case Fail
+
+Upload Post Request
+    [Arguments]  ${uri}  ${timeout}=10  ${quiet}=${QUIET}  &{kwargs}
+
+    # Description of arguments:
+    # uri             URI for uploading image via REST.
+    # timeout         Time allocated for the REST command to return status.
+    # quiet           If enabled turns off logging to console.
+    # kwargs          A dictionary that maps each keyword to a value.
+
+    Initialize OpenBMC  ${timeout}  quiet=${quiet}
+    ${base_uri}=  Catenate  SEPARATOR=  ${DBUS_PREFIX}  ${uri}
+    ${headers}=  Create Dictionary  Content-Type=application/octet-stream
+    ...  Accept=application/octet-stream
+    Set To Dictionary  ${kwargs}  headers  ${headers}
+    Run Keyword If  '${quiet}' == '${0}'  Log Request  method=Post
+    ...  base_uri=${base_uri}  args=&{kwargs}
+    ${ret}=  Post Request  openbmc  ${base_uri}  &{kwargs}  timeout=${timeout}
+    Run Keyword If  '${quiet}' == '${0}'  Log Response  ${ret}
+    Should Be Equal As Strings  ${ret.status_code}  ${HTTP_OK}
diff --git a/lib/resource.txt b/lib/resource.txt
index 247d251..d3c3cb1 100755
--- a/lib/resource.txt
+++ b/lib/resource.txt
@@ -47,6 +47,11 @@
 # BMC debug tarball parameter
 ${DEBUG_TARBALL_PATH}  ${EMPTY}
 
+# Upload Image parameters
+${TFTP_SERVER}       ${EMPTY}
+${TFTP_FILE_NAME}    ${EMPTY}
+${IMAGE_FILE_PATH}   ${EMPTY}
+
 *** Keywords ***
 Get Inventory Schema
     [Arguments]    ${machine}
diff --git a/tools/generate_argumentfile.sh b/tools/generate_argumentfile.sh
index 1a114a3..0529469 100755
--- a/tools/generate_argumentfile.sh
+++ b/tools/generate_argumentfile.sh
@@ -22,3 +22,6 @@
 echo "--variable OS_USERNAME:$OS_USERNAME" >> $ARG_FILE
 echo "--variable OS_PASSWORD:$OS_PASSWORD" >> $ARG_FILE
 echo "--variable DEBUG_TARBALL_PATH:$DEBUG_TARBALL_PATH" >> $ARG_FILE
+echo "--variable TFTP_SERVER:$TFTP_SERVER" >> $ARG_FILE
+echo "--variable TFTP_FILE_NAME:$TFTP_FILE_NAME" >> $ARG_FILE
+echo "--variable IMAGE_FILE_PATH:$IMAGE_FILE_PATH" >> $ARG_FILE