blob: 6b44af9546772cfc1807b051afa182064f72bc66 [file] [log] [blame]
George Keishinge7e91712021-09-03 11:28:44 -05001#!/usr/bin/env python3
Saqib Khanfb1f6ae2017-04-26 13:24:41 -05002
3r"""
Charles Paul Hoferde7d4082017-08-08 14:41:01 -05004This module provides utilities for code updates.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -05005"""
6
Patrick Williams57318182022-12-08 06:18:26 -06007import collections
Saqib Khanfb1f6ae2017-04-26 13:24:41 -05008import os
Saqib Khanfb1f6ae2017-04-26 13:24:41 -05009import re
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050010import sys
Saqib Khanfb1f6ae2017-04-26 13:24:41 -050011import tarfile
12import time
Patrick Williams57318182022-12-08 06:18:26 -060013
14import bmc_ssh_utils as bsu
15import gen_print as gp
16import gen_robot_keyword as keyword
17import variables as var
18from robot.libraries.BuiltIn import BuiltIn
Saqib Khanfb1f6ae2017-04-26 13:24:41 -050019
20robot_pgm_dir_path = os.path.dirname(__file__) + os.sep
Patrick Williams57318182022-12-08 06:18:26 -060021repo_data_path = re.sub("/lib", "/data", robot_pgm_dir_path)
Saqib Khanfb1f6ae2017-04-26 13:24:41 -050022sys.path.append(repo_data_path)
23
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050024
Sushil Singh8469a482020-07-30 04:19:10 -050025def get_bmc_firmware(image_type, sw_dict):
26 r"""
27 Get the dictionary of image based on image type like either BMC or Host.
28
29 Description of argument(s):
30 image_type This value is either BMC update or Host update type.
George Keishing16b3c7b2021-01-28 09:23:37 -060031 sw_dict This contain dictionary of firmware inventory properties.
Sushil Singh8469a482020-07-30 04:19:10 -050032 """
33
34 temp_dict = collections.OrderedDict()
35 for key, value in sw_dict.items():
Patrick Williams57318182022-12-08 06:18:26 -060036 if value["image_type"] == image_type:
Sushil Singh8469a482020-07-30 04:19:10 -050037 temp_dict[key] = value
38 else:
39 pass
40 return temp_dict
41
42
Gunnar Mills096cd562018-03-26 10:19:12 -050043def verify_no_duplicate_image_priorities(image_purpose):
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050044 r"""
45 Check that there are no active images with the same purpose and priority.
46
47 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050048 image_purpose The purpose that images must have to be
49 checked for priority duplicates.
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050050 """
51
52 taken_priorities = {}
Patrick Williams57318182022-12-08 06:18:26 -060053 _, image_names = keyword.run_key(
54 "Get Software Objects " + "version_type=" + image_purpose
55 )
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050056
57 for image_name in image_names:
58 _, image = keyword.run_key("Get Host Software Property " + image_name)
59 if image["Activation"] != var.ACTIVE:
60 continue
61 image_priority = image["Priority"]
62 if image_priority in taken_priorities:
Patrick Williams57318182022-12-08 06:18:26 -060063 BuiltIn().fail(
64 "Found active images with the same priority.\n"
65 + gp.sprint_vars(image, taken_priorities[image_priority])
66 )
Charles Paul Hofera5673162017-08-30 09:49:16 -050067 taken_priorities[image_priority] = image
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050068
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050069
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050070def get_non_running_bmc_software_object():
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050071 r"""
72 Get the URI to a BMC image from software that is not running on the BMC.
73 """
74
75 # Get the version of the image currently running on the BMC.
76 _, cur_img_version = keyword.run_key("Get BMC Version")
77 # Remove the surrounding double quotes from the version.
Patrick Williams57318182022-12-08 06:18:26 -060078 cur_img_version = cur_img_version.replace('"', "")
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050079
Patrick Williams57318182022-12-08 06:18:26 -060080 _, images = keyword.run_key(
81 "Read Properties " + var.SOFTWARE_VERSION_URI + "enumerate"
82 )
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050083
84 for image_name in images:
85 _, image_properties = keyword.run_key(
Patrick Williams57318182022-12-08 06:18:26 -060086 "Get Host Software Property " + image_name
87 )
88 if (
89 "Purpose" in image_properties
90 and "Version" in image_properties
91 and image_properties["Purpose"] != var.VERSION_PURPOSE_HOST
92 and image_properties["Version"] != cur_img_version
93 ):
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050094 return image_name
95 BuiltIn().fail("Did not find any non-running BMC images.")
96
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050097
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050098def delete_all_pnor_images():
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050099 r"""
100 Delete all PNOR images from the BMC.
101 """
102
Adriana Kobylak7eedb1d2019-08-12 15:24:23 -0500103 keyword.run_key("Initiate Host PowerOff")
104
Patrick Williams57318182022-12-08 06:18:26 -0600105 status, images = keyword.run_key(
106 "Get Software Objects " + var.VERSION_PURPOSE_HOST
107 )
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500108 for image_name in images:
Patrick Williams57318182022-12-08 06:18:26 -0600109 keyword.run_key(
110 "Delete Image And Verify "
111 + image_name
112 + " "
113 + var.VERSION_PURPOSE_HOST
114 )
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500115
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500116
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500117def wait_for_activation_state_change(version_id, initial_state):
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500118 r"""
119 Wait for the current activation state of ${version_id} to
120 change from the state provided by the calling function.
121
122 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500123 version_id The version ID whose state change we are
124 waiting for.
125 initial_state The activation state we want to wait for.
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500126 """
127
128 keyword.run_key_u("Open Connection And Log In")
129 retry = 0
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500130 num_read_errors = 0
131 read_fail_threshold = 1
Patrick Williams57318182022-12-08 06:18:26 -0600132 while retry < 60:
133 status, software_state = keyword.run_key(
134 "Read Properties " + var.SOFTWARE_VERSION_URI + str(version_id),
135 ignore=1,
136 )
137 if status == "FAIL":
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500138 num_read_errors += 1
139 if num_read_errors > read_fail_threshold:
Patrick Williams57318182022-12-08 06:18:26 -0600140 message = "Read errors exceeds threshold:\n " + gp.sprint_vars(
141 num_read_errors, read_fail_threshold
142 )
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500143 BuiltIn().fail(message)
Charles Paul Hofer4d26c002017-09-27 08:39:29 -0500144 time.sleep(10)
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500145 continue
146
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500147 current_state = (software_state)["Activation"]
Patrick Williams57318182022-12-08 06:18:26 -0600148 if initial_state == current_state:
Charles Paul Hofer4d26c002017-09-27 08:39:29 -0500149 time.sleep(10)
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500150 retry += 1
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500151 num_read_errors = 0
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500152 else:
153 return
154 return
155
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500156
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500157def get_latest_file(dir_path):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500158 r"""
159 Get the path to the latest uploaded file.
160
161 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500162 dir_path Path to the dir from which the name of the
163 last updated file or folder will be
164 returned to the calling function.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500165 """
166
Patrick Williams57318182022-12-08 06:18:26 -0600167 stdout, stderr, rc = bsu.bmc_execute_command(
168 "cd "
169 + dir_path
170 + "; stat -c '%Y %n' * |"
171 + " sort -k1,1nr | head -n 1"
172 )
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500173 return stdout.split(" ")[-1]
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500174
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500175
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500176def get_version_tar(tar_file_path):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500177 r"""
178 Read the image version from the MANIFEST inside the tarball.
179
180 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500181 tar_file_path The path to a tar file that holds the image
182 version inside the MANIFEST.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500183 """
184
Sushil Singh3c77d782019-10-14 05:29:42 -0500185 version = ""
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500186 tar = tarfile.open(tar_file_path)
187 for member in tar.getmembers():
George Keishing42ade542020-07-22 09:51:46 -0500188 BuiltIn().log_to_console(member.name)
189 if member.name != "MANIFEST":
190 continue
Gunnar Mills096cd562018-03-26 10:19:12 -0500191 f = tar.extractfile(member)
192 content = f.read()
George Keishing36efbc02018-12-12 10:18:23 -0600193 if content.find(b"version=") == -1:
194 # This tar member does not contain the version.
195 continue
George Keishing42ade542020-07-22 09:51:46 -0500196 content = content.decode("utf-8", "ignore").split("\n")
George Keishing36efbc02018-12-12 10:18:23 -0600197 content = [x for x in content if "version=" in x]
198 version = content[0].split("=")[-1]
199 break
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500200 tar.close()
201 return version
202
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500203
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500204def get_image_version(file_path):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500205 r"""
206 Read the file for a version object.
207
208 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500209 file_path The path to a file that holds the image
210 version.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500211 """
212
Patrick Williams57318182022-12-08 06:18:26 -0600213 stdout, stderr, rc = bsu.bmc_execute_command(
214 "cat " + file_path + ' | grep "version="', ignore_err=1
215 )
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500216 return (stdout.split("\n")[0]).split("=")[-1]
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500217
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500218
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500219def get_image_purpose(file_path):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500220 r"""
221 Read the file for a purpose object.
222
223 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500224 file_path The path to a file that holds the image
225 purpose.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500226 """
227
Patrick Williams57318182022-12-08 06:18:26 -0600228 stdout, stderr, rc = bsu.bmc_execute_command(
229 "cat " + file_path + ' | grep "purpose="', ignore_err=1
230 )
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500231 return stdout.split("=")[-1]
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500232
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500233
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500234def get_image_path(image_version):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500235 r"""
236 Query the upload image dir for the presence of image matching
237 the version that was read from the MANIFEST before uploading
238 the image. Based on the purpose verify the activation object
239 exists and is either READY or INVALID.
240
241 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500242 image_version The version of the image that should match
243 one of the images in the upload dir.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500244 """
245
Patrick Williams57318182022-12-08 06:18:26 -0600246 stdout, stderr, rc = bsu.bmc_execute_command(
247 "ls -d " + var.IMAGE_UPLOAD_DIR_PATH + "*/"
248 )
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500249
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500250 image_list = stdout.split("\n")
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500251 retry = 0
Patrick Williams57318182022-12-08 06:18:26 -0600252 while retry < 10:
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500253 for i in range(0, len(image_list)):
254 version = get_image_version(image_list[i] + "MANIFEST")
Patrick Williams57318182022-12-08 06:18:26 -0600255 if version == image_version:
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500256 return image_list[i]
257 time.sleep(10)
258 retry += 1
259
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500260
Patrick Williams57318182022-12-08 06:18:26 -0600261def verify_image_upload(image_version, timeout=3):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500262 r"""
263 Verify the image was uploaded correctly and that it created
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500264 a valid d-bus object. If the first check for the image
265 fails, try again until we reach the timeout.
266
267 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500268 image_version The version from the image's manifest file
269 (e.g. "v2.2-253-g00050f1").
270 timeout How long, in minutes, to keep trying to
271 find the image on the BMC. Default is 3 minutes.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500272 """
273
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500274 image_path = get_image_path(image_version)
275 image_version_id = image_path.split("/")[-2]
276
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500277 keyword.run_key_u("Open Connection And Log In")
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500278 image_purpose = get_image_purpose(image_path + "MANIFEST")
Patrick Williams57318182022-12-08 06:18:26 -0600279 if (
280 image_purpose == var.VERSION_PURPOSE_BMC
281 or image_purpose == var.VERSION_PURPOSE_HOST
282 ):
George Keishingff1e3ec2017-07-20 01:58:21 -0500283 uri = var.SOFTWARE_VERSION_URI + image_version_id
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500284 ret_values = ""
285 for itr in range(timeout * 2):
Patrick Williams57318182022-12-08 06:18:26 -0600286 status, ret_values = keyword.run_key(
287 "Read Attribute " + uri + " Activation"
288 )
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500289
Patrick Williams57318182022-12-08 06:18:26 -0600290 if (
291 (ret_values == var.READY)
292 or (ret_values == var.INVALID)
293 or (ret_values == var.ACTIVE)
294 ):
Charles Paul Hofercef61992017-08-18 10:14:18 -0500295 return True, image_version_id
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500296 else:
297 time.sleep(30)
298
299 # If we exit the for loop, the timeout has been reached
300 gp.print_var(ret_values)
Charles Paul Hofercef61992017-08-18 10:14:18 -0500301 return False, None
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500302 else:
Saqib Khanbb8b63f2017-05-24 10:58:01 -0500303 gp.print_var(image_purpose)
Charles Paul Hofercef61992017-08-18 10:14:18 -0500304 return False, None
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500305
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500306
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500307def verify_image_not_in_bmc_uploads_dir(image_version, timeout=3):
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500308 r"""
309 Check that an image with the given version is not unpacked inside of the
310 BMCs image uploads directory. If no image is found, retry every 30 seconds
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500311 until the given timeout is hit, in case the BMC takes time
312 unpacking the image.
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500313
314 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500315 image_version The version of the image to look for on
316 the BMC.
317 timeout How long, in minutes, to try to find an
318 image on the BMC. Default is 3 minutes.
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500319 """
320
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500321 for i in range(timeout * 2):
Patrick Williams57318182022-12-08 06:18:26 -0600322 stdout, stderr, rc = bsu.bmc_execute_command(
323 "ls "
324 + var.IMAGE_UPLOAD_DIR_PATH
325 + "*/MANIFEST 2>/dev/null "
326 + '| xargs grep -rl "version='
327 + image_version
328 + '"'
329 )
330 image_dir = os.path.dirname(stdout.split("\n")[0])
331 if "" != image_dir:
332 bsu.bmc_execute_command("rm -rf " + image_dir)
333 BuiltIn().fail("Found invalid BMC Image: " + image_dir)
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500334 time.sleep(30)