1e7e9171eSGeorge Keishing#!/usr/bin/env python3 2de7d408dSCharles Paul Hofer 3de7d408dSCharles Paul Hoferr""" 4de7d408dSCharles Paul HoferThis module provides utilities for code updates. 5de7d408dSCharles Paul Hofer""" 6de7d408dSCharles Paul Hofer 7*20f38712SPatrick Williamsimport collections 8de7d408dSCharles Paul Hoferimport os 9de7d408dSCharles Paul Hoferimport re 10de7d408dSCharles Paul Hoferimport sys 11de7d408dSCharles Paul Hoferimport tarfile 12de7d408dSCharles Paul Hoferimport time 13*20f38712SPatrick Williams 1437c58c8cSGeorge Keishingfrom robot.libraries.BuiltIn import BuiltIn 15de7d408dSCharles Paul Hofer 16de7d408dSCharles Paul Hoferrobot_pgm_dir_path = os.path.dirname(__file__) + os.sep 17*20f38712SPatrick Williamsrepo_data_path = re.sub("/lib", "/data", robot_pgm_dir_path) 18de7d408dSCharles Paul Hofersys.path.append(repo_data_path) 19de7d408dSCharles Paul Hofer 2009679890SGeorge Keishingimport bmc_ssh_utils as bsu # NOQA 2109679890SGeorge Keishingimport gen_print as gp # NOQA 22*20f38712SPatrick Williamsimport gen_robot_keyword as keyword # NOQA 2309679890SGeorge Keishingimport variables as var # NOQA 2437c58c8cSGeorge Keishing 25c1fa2bc9SCharles Paul Hofer 268469a485SSushil Singhdef get_bmc_firmware(image_type, sw_dict): 278469a485SSushil Singh r""" 288469a485SSushil Singh Get the dictionary of image based on image type like either BMC or Host. 298469a485SSushil Singh 308469a485SSushil Singh Description of argument(s): 318469a485SSushil Singh image_type This value is either BMC update or Host update type. 3216b3c7bfSGeorge Keishing sw_dict This contain dictionary of firmware inventory properties. 338469a485SSushil Singh """ 348469a485SSushil Singh 358469a485SSushil Singh temp_dict = collections.OrderedDict() 368469a485SSushil Singh for key, value in sw_dict.items(): 37*20f38712SPatrick Williams if value["image_type"] == image_type: 388469a485SSushil Singh temp_dict[key] = value 398469a485SSushil Singh else: 408469a485SSushil Singh pass 418469a485SSushil Singh return temp_dict 428469a485SSushil Singh 438469a485SSushil Singh 44096cd565SGunnar Millsdef verify_no_duplicate_image_priorities(image_purpose): 45c1fa2bc9SCharles Paul Hofer r""" 46c1fa2bc9SCharles Paul Hofer Check that there are no active images with the same purpose and priority. 47c1fa2bc9SCharles Paul Hofer 48c1fa2bc9SCharles Paul Hofer Description of argument(s): 49004ad3c9SJoy Onyerikwu image_purpose The purpose that images must have to be 50004ad3c9SJoy Onyerikwu checked for priority duplicates. 51c1fa2bc9SCharles Paul Hofer """ 52c1fa2bc9SCharles Paul Hofer 53c1fa2bc9SCharles Paul Hofer taken_priorities = {} 54*20f38712SPatrick Williams _, image_names = keyword.run_key( 55*20f38712SPatrick Williams "Get Software Objects " + "version_type=" + image_purpose 56*20f38712SPatrick Williams ) 57c1fa2bc9SCharles Paul Hofer 58c1fa2bc9SCharles Paul Hofer for image_name in image_names: 59c1fa2bc9SCharles Paul Hofer _, image = keyword.run_key("Get Host Software Property " + image_name) 60c1fa2bc9SCharles Paul Hofer if image["Activation"] != var.ACTIVE: 61c1fa2bc9SCharles Paul Hofer continue 62c1fa2bc9SCharles Paul Hofer image_priority = image["Priority"] 63c1fa2bc9SCharles Paul Hofer if image_priority in taken_priorities: 64*20f38712SPatrick Williams BuiltIn().fail( 65*20f38712SPatrick Williams "Found active images with the same priority.\n" 66*20f38712SPatrick Williams + gp.sprint_vars(image, taken_priorities[image_priority]) 67*20f38712SPatrick Williams ) 68c1fa2bc9SCharles Paul Hofer taken_priorities[image_priority] = image 69c1fa2bc9SCharles Paul Hofer 70c1fa2bc9SCharles Paul Hofer 71da24d0a0SCharles Paul Hoferdef get_non_running_bmc_software_object(): 72da24d0a0SCharles Paul Hofer r""" 73da24d0a0SCharles Paul Hofer Get the URI to a BMC image from software that is not running on the BMC. 74da24d0a0SCharles Paul Hofer """ 75da24d0a0SCharles Paul Hofer 76da24d0a0SCharles Paul Hofer # Get the version of the image currently running on the BMC. 77da24d0a0SCharles Paul Hofer _, cur_img_version = keyword.run_key("Get BMC Version") 78da24d0a0SCharles Paul Hofer # Remove the surrounding double quotes from the version. 79*20f38712SPatrick Williams cur_img_version = cur_img_version.replace('"', "") 80da24d0a0SCharles Paul Hofer 81*20f38712SPatrick Williams _, images = keyword.run_key( 82*20f38712SPatrick Williams "Read Properties " + var.SOFTWARE_VERSION_URI + "enumerate" 83*20f38712SPatrick Williams ) 84da24d0a0SCharles Paul Hofer 85da24d0a0SCharles Paul Hofer for image_name in images: 86da24d0a0SCharles Paul Hofer _, image_properties = keyword.run_key( 87*20f38712SPatrick Williams "Get Host Software Property " + image_name 88*20f38712SPatrick Williams ) 89*20f38712SPatrick Williams if ( 90*20f38712SPatrick Williams "Purpose" in image_properties 91*20f38712SPatrick Williams and "Version" in image_properties 92*20f38712SPatrick Williams and image_properties["Purpose"] != var.VERSION_PURPOSE_HOST 93*20f38712SPatrick Williams and image_properties["Version"] != cur_img_version 94*20f38712SPatrick Williams ): 95da24d0a0SCharles Paul Hofer return image_name 96da24d0a0SCharles Paul Hofer BuiltIn().fail("Did not find any non-running BMC images.") 97da24d0a0SCharles Paul Hofer 98da24d0a0SCharles Paul Hofer 99de7d408dSCharles Paul Hoferdef delete_all_pnor_images(): 100de7d408dSCharles Paul Hofer r""" 101de7d408dSCharles Paul Hofer Delete all PNOR images from the BMC. 102de7d408dSCharles Paul Hofer """ 103de7d408dSCharles Paul Hofer 1047eedb1ddSAdriana Kobylak keyword.run_key("Initiate Host PowerOff") 1057eedb1ddSAdriana Kobylak 106*20f38712SPatrick Williams status, images = keyword.run_key( 107*20f38712SPatrick Williams "Get Software Objects " + var.VERSION_PURPOSE_HOST 108*20f38712SPatrick Williams ) 109de7d408dSCharles Paul Hofer for image_name in images: 110*20f38712SPatrick Williams keyword.run_key( 111*20f38712SPatrick Williams "Delete Image And Verify " 112*20f38712SPatrick Williams + image_name 113*20f38712SPatrick Williams + " " 114*20f38712SPatrick Williams + var.VERSION_PURPOSE_HOST 115*20f38712SPatrick Williams ) 116de7d408dSCharles Paul Hofer 117de7d408dSCharles Paul Hofer 118de7d408dSCharles Paul Hoferdef wait_for_activation_state_change(version_id, initial_state): 119de7d408dSCharles Paul Hofer r""" 120de7d408dSCharles Paul Hofer Wait for the current activation state of ${version_id} to 121de7d408dSCharles Paul Hofer change from the state provided by the calling function. 122de7d408dSCharles Paul Hofer 123de7d408dSCharles Paul Hofer Description of argument(s): 124004ad3c9SJoy Onyerikwu version_id The version ID whose state change we are 125004ad3c9SJoy Onyerikwu waiting for. 126de7d408dSCharles Paul Hofer initial_state The activation state we want to wait for. 127de7d408dSCharles Paul Hofer """ 128de7d408dSCharles Paul Hofer 129de7d408dSCharles Paul Hofer keyword.run_key_u("Open Connection And Log In") 130de7d408dSCharles Paul Hofer retry = 0 131290b8bd2SCharles Paul Hofer num_read_errors = 0 132290b8bd2SCharles Paul Hofer read_fail_threshold = 1 133*20f38712SPatrick Williams while retry < 60: 134*20f38712SPatrick Williams status, software_state = keyword.run_key( 135*20f38712SPatrick Williams "Read Properties " + var.SOFTWARE_VERSION_URI + str(version_id), 136*20f38712SPatrick Williams ignore=1, 137*20f38712SPatrick Williams ) 138*20f38712SPatrick Williams if status == "FAIL": 139290b8bd2SCharles Paul Hofer num_read_errors += 1 140290b8bd2SCharles Paul Hofer if num_read_errors > read_fail_threshold: 141*20f38712SPatrick Williams message = "Read errors exceeds threshold:\n " + gp.sprint_vars( 142*20f38712SPatrick Williams num_read_errors, read_fail_threshold 143*20f38712SPatrick Williams ) 144290b8bd2SCharles Paul Hofer BuiltIn().fail(message) 1454d26c008SCharles Paul Hofer time.sleep(10) 146290b8bd2SCharles Paul Hofer continue 147290b8bd2SCharles Paul Hofer 148de7d408dSCharles Paul Hofer current_state = (software_state)["Activation"] 149*20f38712SPatrick Williams if initial_state == current_state: 1504d26c008SCharles Paul Hofer time.sleep(10) 151de7d408dSCharles Paul Hofer retry += 1 152290b8bd2SCharles Paul Hofer num_read_errors = 0 153de7d408dSCharles Paul Hofer else: 154de7d408dSCharles Paul Hofer return 155de7d408dSCharles Paul Hofer return 156de7d408dSCharles Paul Hofer 157de7d408dSCharles Paul Hofer 158de7d408dSCharles Paul Hoferdef get_latest_file(dir_path): 159de7d408dSCharles Paul Hofer r""" 160de7d408dSCharles Paul Hofer Get the path to the latest uploaded file. 161de7d408dSCharles Paul Hofer 162de7d408dSCharles Paul Hofer Description of argument(s): 163004ad3c9SJoy Onyerikwu dir_path Path to the dir from which the name of the 164004ad3c9SJoy Onyerikwu last updated file or folder will be 165004ad3c9SJoy Onyerikwu returned to the calling function. 166de7d408dSCharles Paul Hofer """ 167de7d408dSCharles Paul Hofer 168*20f38712SPatrick Williams stdout, stderr, rc = bsu.bmc_execute_command( 169*20f38712SPatrick Williams "cd " 170*20f38712SPatrick Williams + dir_path 171004ad3c9SJoy Onyerikwu + "; stat -c '%Y %n' * |" 172*20f38712SPatrick Williams + " sort -k1,1nr | head -n 1" 173*20f38712SPatrick Williams ) 1749b66897bSJoy Onyerikwu return stdout.split(" ")[-1] 175de7d408dSCharles Paul Hofer 176de7d408dSCharles Paul Hofer 177de7d408dSCharles Paul Hoferdef get_version_tar(tar_file_path): 178de7d408dSCharles Paul Hofer r""" 179de7d408dSCharles Paul Hofer Read the image version from the MANIFEST inside the tarball. 180de7d408dSCharles Paul Hofer 181de7d408dSCharles Paul Hofer Description of argument(s): 182de7d408dSCharles Paul Hofer tar_file_path The path to a tar file that holds the image 183de7d408dSCharles Paul Hofer version inside the MANIFEST. 184de7d408dSCharles Paul Hofer """ 185de7d408dSCharles Paul Hofer 1863c77d78fSSushil Singh version = "" 187de7d408dSCharles Paul Hofer tar = tarfile.open(tar_file_path) 188de7d408dSCharles Paul Hofer for member in tar.getmembers(): 18942ade549SGeorge Keishing BuiltIn().log_to_console(member.name) 19042ade549SGeorge Keishing if member.name != "MANIFEST": 19142ade549SGeorge Keishing continue 192de7d408dSCharles Paul Hofer f = tar.extractfile(member) 193de7d408dSCharles Paul Hofer content = f.read() 19436efbc04SGeorge Keishing if content.find(b"version=") == -1: 19536efbc04SGeorge Keishing # This tar member does not contain the version. 19636efbc04SGeorge Keishing continue 19742ade549SGeorge Keishing content = content.decode("utf-8", "ignore").split("\n") 198de7d408dSCharles Paul Hofer content = [x for x in content if "version=" in x] 199de7d408dSCharles Paul Hofer version = content[0].split("=")[-1] 200de7d408dSCharles Paul Hofer break 201de7d408dSCharles Paul Hofer tar.close() 202de7d408dSCharles Paul Hofer return version 203de7d408dSCharles Paul Hofer 204de7d408dSCharles Paul Hofer 205de7d408dSCharles Paul Hoferdef get_image_version(file_path): 206de7d408dSCharles Paul Hofer r""" 207de7d408dSCharles Paul Hofer Read the file for a version object. 208de7d408dSCharles Paul Hofer 209de7d408dSCharles Paul Hofer Description of argument(s): 210004ad3c9SJoy Onyerikwu file_path The path to a file that holds the image 211004ad3c9SJoy Onyerikwu version. 212de7d408dSCharles Paul Hofer """ 213de7d408dSCharles Paul Hofer 214*20f38712SPatrick Williams stdout, stderr, rc = bsu.bmc_execute_command( 215*20f38712SPatrick Williams "cat " + file_path + ' | grep "version="', ignore_err=1 216*20f38712SPatrick Williams ) 2179b66897bSJoy Onyerikwu return (stdout.split("\n")[0]).split("=")[-1] 218de7d408dSCharles Paul Hofer 219de7d408dSCharles Paul Hofer 220de7d408dSCharles Paul Hoferdef get_image_purpose(file_path): 221de7d408dSCharles Paul Hofer r""" 222de7d408dSCharles Paul Hofer Read the file for a purpose object. 223de7d408dSCharles Paul Hofer 224de7d408dSCharles Paul Hofer Description of argument(s): 225004ad3c9SJoy Onyerikwu file_path The path to a file that holds the image 226004ad3c9SJoy Onyerikwu purpose. 227de7d408dSCharles Paul Hofer """ 228de7d408dSCharles Paul Hofer 229*20f38712SPatrick Williams stdout, stderr, rc = bsu.bmc_execute_command( 230*20f38712SPatrick Williams "cat " + file_path + ' | grep "purpose="', ignore_err=1 231*20f38712SPatrick Williams ) 2329b66897bSJoy Onyerikwu return stdout.split("=")[-1] 233de7d408dSCharles Paul Hofer 234de7d408dSCharles Paul Hofer 235de7d408dSCharles Paul Hoferdef get_image_path(image_version): 236de7d408dSCharles Paul Hofer r""" 237de7d408dSCharles Paul Hofer Query the upload image dir for the presence of image matching 238de7d408dSCharles Paul Hofer the version that was read from the MANIFEST before uploading 239de7d408dSCharles Paul Hofer the image. Based on the purpose verify the activation object 240de7d408dSCharles Paul Hofer exists and is either READY or INVALID. 241de7d408dSCharles Paul Hofer 242de7d408dSCharles Paul Hofer Description of argument(s): 243004ad3c9SJoy Onyerikwu image_version The version of the image that should match 244004ad3c9SJoy Onyerikwu one of the images in the upload dir. 245de7d408dSCharles Paul Hofer """ 246de7d408dSCharles Paul Hofer 247*20f38712SPatrick Williams stdout, stderr, rc = bsu.bmc_execute_command( 248*20f38712SPatrick Williams "ls -d " + var.IMAGE_UPLOAD_DIR_PATH + "*/" 249*20f38712SPatrick Williams ) 250de7d408dSCharles Paul Hofer 2519b66897bSJoy Onyerikwu image_list = stdout.split("\n") 252de7d408dSCharles Paul Hofer retry = 0 253*20f38712SPatrick Williams while retry < 10: 254de7d408dSCharles Paul Hofer for i in range(0, len(image_list)): 255de7d408dSCharles Paul Hofer version = get_image_version(image_list[i] + "MANIFEST") 256*20f38712SPatrick Williams if version == image_version: 257de7d408dSCharles Paul Hofer return image_list[i] 258de7d408dSCharles Paul Hofer time.sleep(10) 259de7d408dSCharles Paul Hofer retry += 1 260de7d408dSCharles Paul Hofer 261de7d408dSCharles Paul Hofer 262*20f38712SPatrick Williamsdef verify_image_upload(image_version, timeout=3): 263de7d408dSCharles Paul Hofer r""" 264de7d408dSCharles Paul Hofer Verify the image was uploaded correctly and that it created 265de7d408dSCharles Paul Hofer a valid d-bus object. If the first check for the image 266de7d408dSCharles Paul Hofer fails, try again until we reach the timeout. 267de7d408dSCharles Paul Hofer 268de7d408dSCharles Paul Hofer Description of argument(s): 2699f74d3afSCharles Paul Hofer image_version The version from the image's manifest file 270e0a81289SGeorge Keishing (e.g. "v2.2-253-g00050f1"). 271004ad3c9SJoy Onyerikwu timeout How long, in minutes, to keep trying to 272004ad3c9SJoy Onyerikwu find the image on the BMC. Default is 3 minutes. 273de7d408dSCharles Paul Hofer """ 274de7d408dSCharles Paul Hofer 275de7d408dSCharles Paul Hofer image_path = get_image_path(image_version) 276de7d408dSCharles Paul Hofer image_version_id = image_path.split("/")[-2] 277de7d408dSCharles Paul Hofer 278de7d408dSCharles Paul Hofer keyword.run_key_u("Open Connection And Log In") 279de7d408dSCharles Paul Hofer image_purpose = get_image_purpose(image_path + "MANIFEST") 280*20f38712SPatrick Williams if ( 281*20f38712SPatrick Williams image_purpose == var.VERSION_PURPOSE_BMC 282*20f38712SPatrick Williams or image_purpose == var.VERSION_PURPOSE_HOST 283*20f38712SPatrick Williams ): 284de7d408dSCharles Paul Hofer uri = var.SOFTWARE_VERSION_URI + image_version_id 285de7d408dSCharles Paul Hofer ret_values = "" 286de7d408dSCharles Paul Hofer for itr in range(timeout * 2): 287*20f38712SPatrick Williams status, ret_values = keyword.run_key( 288*20f38712SPatrick Williams "Read Attribute " + uri + " Activation" 289*20f38712SPatrick Williams ) 290de7d408dSCharles Paul Hofer 291*20f38712SPatrick Williams if ( 292*20f38712SPatrick Williams (ret_values == var.READY) 293*20f38712SPatrick Williams or (ret_values == var.INVALID) 294*20f38712SPatrick Williams or (ret_values == var.ACTIVE) 295*20f38712SPatrick Williams ): 296cef6199aSCharles Paul Hofer return True, image_version_id 297de7d408dSCharles Paul Hofer else: 298de7d408dSCharles Paul Hofer time.sleep(30) 299de7d408dSCharles Paul Hofer 300de7d408dSCharles Paul Hofer # If we exit the for loop, the timeout has been reached 301de7d408dSCharles Paul Hofer gp.print_var(ret_values) 302cef6199aSCharles Paul Hofer return False, None 303de7d408dSCharles Paul Hofer else: 304de7d408dSCharles Paul Hofer gp.print_var(image_purpose) 305cef6199aSCharles Paul Hofer return False, None 306de7d408dSCharles Paul Hofer 307de7d408dSCharles Paul Hofer 308de7d408dSCharles Paul Hoferdef verify_image_not_in_bmc_uploads_dir(image_version, timeout=3): 309de7d408dSCharles Paul Hofer r""" 310de7d408dSCharles Paul Hofer Check that an image with the given version is not unpacked inside of the 311de7d408dSCharles Paul Hofer BMCs image uploads directory. If no image is found, retry every 30 seconds 312de7d408dSCharles Paul Hofer until the given timeout is hit, in case the BMC takes time 313de7d408dSCharles Paul Hofer unpacking the image. 314de7d408dSCharles Paul Hofer 315de7d408dSCharles Paul Hofer Description of argument(s): 316004ad3c9SJoy Onyerikwu image_version The version of the image to look for on 317004ad3c9SJoy Onyerikwu the BMC. 318004ad3c9SJoy Onyerikwu timeout How long, in minutes, to try to find an 319004ad3c9SJoy Onyerikwu image on the BMC. Default is 3 minutes. 320de7d408dSCharles Paul Hofer """ 321de7d408dSCharles Paul Hofer 322de7d408dSCharles Paul Hofer for i in range(timeout * 2): 323*20f38712SPatrick Williams stdout, stderr, rc = bsu.bmc_execute_command( 324*20f38712SPatrick Williams "ls " 325*20f38712SPatrick Williams + var.IMAGE_UPLOAD_DIR_PATH 326*20f38712SPatrick Williams + "*/MANIFEST 2>/dev/null " 327004ad3c9SJoy Onyerikwu + '| xargs grep -rl "version=' 328*20f38712SPatrick Williams + image_version 329*20f38712SPatrick Williams + '"' 330*20f38712SPatrick Williams ) 331*20f38712SPatrick Williams image_dir = os.path.dirname(stdout.split("\n")[0]) 332*20f38712SPatrick Williams if "" != image_dir: 333*20f38712SPatrick Williams bsu.bmc_execute_command("rm -rf " + image_dir) 334*20f38712SPatrick Williams BuiltIn().fail("Found invalid BMC Image: " + image_dir) 335de7d408dSCharles Paul Hofer time.sleep(30) 336