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