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