1#!/usr/bin/env python 2 3r""" 4This module provides utilities for code updates. 5""" 6 7import os 8import re 9import sys 10import tarfile 11import time 12 13robot_pgm_dir_path = os.path.dirname(__file__) + os.sep 14repo_data_path = re.sub('/lib', '/data', robot_pgm_dir_path) 15sys.path.append(repo_data_path) 16 17import gen_robot_keyword as keyword 18import gen_print as gp 19import variables as var 20from robot.libraries.BuiltIn import BuiltIn 21 22 23def verify_no_duplicate_image_priorities(image_purpose): 24 r""" 25 Check that there are no active images with the same purpose and priority. 26 27 Description of argument(s): 28 image_purpose The purpose that images must have to be checked for 29 priority duplicates. 30 """ 31 32 taken_priorities = {} 33 _, image_names = keyword.run_key("Get Software Objects " 34 + "version_type=" + image_purpose) 35 36 for image_name in image_names: 37 _, image = keyword.run_key("Get Host Software Property " + image_name) 38 if image["Activation"] != var.ACTIVE: 39 continue 40 image_priority = image["Priority"] 41 if image_priority in taken_priorities: 42 BuiltIn().fail("Found active images with the same priority.\n" 43 + gp.sprint_vars(image, taken_priorities[image_priority])) 44 taken_priorities[image_priority] = image 45 46 47def get_non_running_bmc_software_object(): 48 r""" 49 Get the URI to a BMC image from software that is not running on the BMC. 50 """ 51 52 # Get the version of the image currently running on the BMC. 53 _, cur_img_version = keyword.run_key("Get BMC Version") 54 # Remove the surrounding double quotes from the version. 55 cur_img_version = cur_img_version.replace('"', '') 56 57 _, images = keyword.run_key("Read Properties " 58 + var.SOFTWARE_VERSION_URI + "enumerate") 59 60 for image_name in images: 61 _, image_properties = keyword.run_key( 62 "Get Host Software Property " + image_name) 63 if 'Purpose' in image_properties and 'Version' in image_properties \ 64 and image_properties['Purpose'] != var.VERSION_PURPOSE_HOST \ 65 and image_properties['Version'] != cur_img_version: 66 return image_name 67 BuiltIn().fail("Did not find any non-running BMC images.") 68 69 70def delete_all_pnor_images(): 71 r""" 72 Delete all PNOR images from the BMC. 73 """ 74 75 status, images = keyword.run_key("Get Software Objects " 76 + var.VERSION_PURPOSE_HOST) 77 for image_name in images: 78 BuiltIn().log_to_console(image_name) 79 # Delete twice, in case the image is in the /tmp/images directory 80 keyword.run_key("Call Method " + image_name 81 + " delete data={\"data\":[]}") 82 keyword.run_key("Call Method " + image_name 83 + " delete data={\"data\":[]}") 84 85 86def wait_for_activation_state_change(version_id, initial_state): 87 r""" 88 Wait for the current activation state of ${version_id} to 89 change from the state provided by the calling function. 90 91 Description of argument(s): 92 version_id The version ID whose state change we are waiting for. 93 initial_state The activation state we want to wait for. 94 """ 95 96 keyword.run_key_u("Open Connection And Log In") 97 retry = 0 98 num_read_errors = 0 99 read_fail_threshold = 1 100 while (retry < 30): 101 # TODO: Use retry option in run_key when available. 102 status, software_state = keyword.run_key("Read Properties " + 103 var.SOFTWARE_VERSION_URI + 104 str(version_id), 105 ignore=1) 106 if status == 'FAIL': 107 num_read_errors += 1 108 if num_read_errors > read_fail_threshold: 109 message = "Read errors exceeds threshold:\n " \ 110 + gp.sprint_vars(num_read_errors, read_fail_threshold) 111 BuiltIn().fail(message) 112 time.sleep(10) 113 continue 114 115 current_state = (software_state)["Activation"] 116 if (initial_state == current_state): 117 time.sleep(10) 118 retry += 1 119 num_read_errors = 0 120 else: 121 return 122 return 123 124 125def get_latest_file(dir_path): 126 r""" 127 Get the path to the latest uploaded file. 128 129 Description of argument(s): 130 dir_path Path to the dir from which the name of the last 131 updated file or folder will be returned to the 132 calling function. 133 """ 134 135 keyword.run_key_u("Open Connection And Log In") 136 status, ret_values =\ 137 keyword.run_key("Execute Command On BMC cd " + dir_path 138 + "; stat -c '%Y %n' * | sort -k1,1nr | head -n 1", ignore=1) 139 return ret_values.split(" ")[-1] 140 141 142def get_version_tar(tar_file_path): 143 r""" 144 Read the image version from the MANIFEST inside the tarball. 145 146 Description of argument(s): 147 tar_file_path The path to a tar file that holds the image 148 version inside the MANIFEST. 149 """ 150 151 tar = tarfile.open(tar_file_path) 152 for member in tar.getmembers(): 153 f = tar.extractfile(member) 154 content = f.read() 155 if "version=" in content: 156 content = content.split("\n") 157 content = [x for x in content if "version=" in x] 158 version = content[0].split("=")[-1] 159 break 160 tar.close() 161 return version 162 163 164def get_image_version(file_path): 165 r""" 166 Read the file for a version object. 167 168 Description of argument(s): 169 file_path The path to a file that holds the image version. 170 """ 171 172 keyword.run_key_u("Open Connection And Log In") 173 status, ret_values =\ 174 keyword.run_key("Execute Command On BMC cat " 175 + file_path + " | grep \"version=\"", ignore=1) 176 return (ret_values.split("\n")[0]).split("=")[-1] 177 178 179def get_image_purpose(file_path): 180 r""" 181 Read the file for a purpose object. 182 183 Description of argument(s): 184 file_path The path to a file that holds the image purpose. 185 """ 186 187 keyword.run_key_u("Open Connection And Log In") 188 status, ret_values =\ 189 keyword.run_key("Execute Command On BMC cat " 190 + file_path + " | grep \"purpose=\"", ignore=1) 191 return ret_values.split("=")[-1] 192 193 194def get_image_path(image_version): 195 r""" 196 Query the upload image dir for the presence of image matching 197 the version that was read from the MANIFEST before uploading 198 the image. Based on the purpose verify the activation object 199 exists and is either READY or INVALID. 200 201 Description of argument(s): 202 image_version The version of the image that should match one 203 of the images in the upload dir. 204 """ 205 206 keyword.run_key_u("Open Connection And Log In") 207 status, image_list =\ 208 keyword.run_key("Execute Command On BMC ls -d " 209 + var.IMAGE_UPLOAD_DIR_PATH + "*/") 210 211 image_list = image_list.split("\n") 212 retry = 0 213 while (retry < 10): 214 for i in range(0, len(image_list)): 215 version = get_image_version(image_list[i] + "MANIFEST") 216 if (version == image_version): 217 return image_list[i] 218 time.sleep(10) 219 retry += 1 220 221 222def verify_image_upload(image_version, 223 timeout=3): 224 r""" 225 Verify the image was uploaded correctly and that it created 226 a valid d-bus object. If the first check for the image 227 fails, try again until we reach the timeout. 228 229 Description of argument(s): 230 image_version The version from the image's manifest file 231 (e.g. "IBM-witherspoon-redbud-ibm-OP9_v1.17_1.68"). 232 timeout How long, in minutes, to keep trying to find the 233 image on the BMC. Default is 3 minutes. 234 """ 235 236 image_path = get_image_path(image_version) 237 image_version_id = image_path.split("/")[-2] 238 239 keyword.run_key_u("Open Connection And Log In") 240 image_purpose = get_image_purpose(image_path + "MANIFEST") 241 if (image_purpose == var.VERSION_PURPOSE_BMC or 242 image_purpose == var.VERSION_PURPOSE_HOST): 243 uri = var.SOFTWARE_VERSION_URI + image_version_id 244 ret_values = "" 245 for itr in range(timeout * 2): 246 status, ret_values = \ 247 keyword.run_key("Read Attribute " + uri + " Activation") 248 249 if ((ret_values == var.READY) or (ret_values == var.INVALID) 250 or (ret_values == var.ACTIVE)): 251 return True, image_version_id 252 else: 253 time.sleep(30) 254 255 # If we exit the for loop, the timeout has been reached 256 gp.print_var(ret_values) 257 return False, None 258 else: 259 gp.print_var(image_purpose) 260 return False, None 261 262 263def verify_image_not_in_bmc_uploads_dir(image_version, timeout=3): 264 r""" 265 Check that an image with the given version is not unpacked inside of the 266 BMCs image uploads directory. If no image is found, retry every 30 seconds 267 until the given timeout is hit, in case the BMC takes time 268 unpacking the image. 269 270 Description of argument(s): 271 image_version The version of the image to look for on the BMC. 272 timeout How long, in minutes, to try to find an image on the BMC. 273 Default is 3 minutes. 274 """ 275 276 keyword.run_key('Open Connection And Log In') 277 for i in range(timeout * 2): 278 stat, grep_res = keyword.run_key('Execute Command On BMC ' 279 + 'ls ' + var.IMAGE_UPLOAD_DIR_PATH + '*/MANIFEST 2>/dev/null ' 280 + '| xargs grep -rl "version=' + image_version + '"') 281 image_dir = os.path.dirname(grep_res.split('\n')[0]) 282 if '' != image_dir: 283 keyword.run_key('Execute Command On BMC rm -rf ' + image_dir) 284 BuiltIn().fail('Found invalid BMC Image: ' + image_dir) 285 time.sleep(30) 286