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