blob: 212dc3d9b7816105b24244f038d5679e81ef6d28 [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
George Keishing09679892022-12-08 08:21:52 -06007from robot.libraries.BuiltIn import BuiltIn
8
Saqib Khanfb1f6ae2017-04-26 13:24:41 -05009import os
Saqib Khanfb1f6ae2017-04-26 13:24:41 -050010import re
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050011import sys
Saqib Khanfb1f6ae2017-04-26 13:24:41 -050012import tarfile
13import time
George Keishinge635ddc2022-12-08 07:38:02 -060014import collections
George Keishing37c58c82022-12-08 07:42:54 -060015from robot.libraries.BuiltIn import BuiltIn
Saqib Khanfb1f6ae2017-04-26 13:24:41 -050016
17robot_pgm_dir_path = os.path.dirname(__file__) + os.sep
George Keishinge635ddc2022-12-08 07:38:02 -060018repo_data_path = re.sub('/lib', '/data', robot_pgm_dir_path)
Saqib Khanfb1f6ae2017-04-26 13:24:41 -050019sys.path.append(repo_data_path)
20
George Keishing09679892022-12-08 08:21:52 -060021import bmc_ssh_utils as bsu # NOQA
22import gen_robot_keyword as keyword # NOQA
23import gen_print as gp # NOQA
24import variables as var # NOQA
George Keishing37c58c82022-12-08 07:42:54 -060025
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050026
Sushil Singh8469a482020-07-30 04:19:10 -050027def get_bmc_firmware(image_type, sw_dict):
28 r"""
29 Get the dictionary of image based on image type like either BMC or Host.
30
31 Description of argument(s):
32 image_type This value is either BMC update or Host update type.
George Keishing16b3c7b2021-01-28 09:23:37 -060033 sw_dict This contain dictionary of firmware inventory properties.
Sushil Singh8469a482020-07-30 04:19:10 -050034 """
35
36 temp_dict = collections.OrderedDict()
37 for key, value in sw_dict.items():
George Keishinge635ddc2022-12-08 07:38:02 -060038 if value['image_type'] == image_type:
Sushil Singh8469a482020-07-30 04:19:10 -050039 temp_dict[key] = value
40 else:
41 pass
42 return temp_dict
43
44
Gunnar Mills096cd562018-03-26 10:19:12 -050045def verify_no_duplicate_image_priorities(image_purpose):
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050046 r"""
47 Check that there are no active images with the same purpose and priority.
48
49 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050050 image_purpose The purpose that images must have to be
51 checked for priority duplicates.
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050052 """
53
54 taken_priorities = {}
George Keishinge635ddc2022-12-08 07:38:02 -060055 _, image_names = keyword.run_key("Get Software Objects "
56 + "version_type=" + image_purpose)
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050057
58 for image_name in image_names:
59 _, image = keyword.run_key("Get Host Software Property " + image_name)
60 if image["Activation"] != var.ACTIVE:
61 continue
62 image_priority = image["Priority"]
63 if image_priority in taken_priorities:
George Keishinge635ddc2022-12-08 07:38:02 -060064 BuiltIn().fail("Found active images with the same priority.\n"
65 + gp.sprint_vars(image,
66 taken_priorities[image_priority]))
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.
George Keishinge635ddc2022-12-08 07:38:02 -060078 cur_img_version = cur_img_version.replace('"', '')
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050079
George Keishinge635ddc2022-12-08 07:38:02 -060080 _, images = keyword.run_key("Read Properties "
81 + var.SOFTWARE_VERSION_URI + "enumerate")
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050082
83 for image_name in images:
84 _, image_properties = keyword.run_key(
George Keishinge635ddc2022-12-08 07:38:02 -060085 "Get Host Software Property " + image_name)
86 if 'Purpose' in image_properties and 'Version' in image_properties \
87 and image_properties['Purpose'] != var.VERSION_PURPOSE_HOST \
88 and image_properties['Version'] != cur_img_version:
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050089 return image_name
90 BuiltIn().fail("Did not find any non-running BMC images.")
91
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050092
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050093def delete_all_pnor_images():
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050094 r"""
95 Delete all PNOR images from the BMC.
96 """
97
Adriana Kobylak7eedb1d2019-08-12 15:24:23 -050098 keyword.run_key("Initiate Host PowerOff")
99
George Keishinge635ddc2022-12-08 07:38:02 -0600100 status, images = keyword.run_key("Get Software Objects "
101 + var.VERSION_PURPOSE_HOST)
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500102 for image_name in images:
George Keishinge635ddc2022-12-08 07:38:02 -0600103 keyword.run_key("Delete Image And Verify " + image_name + " "
104 + var.VERSION_PURPOSE_HOST)
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500105
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500106
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500107def wait_for_activation_state_change(version_id, initial_state):
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500108 r"""
109 Wait for the current activation state of ${version_id} to
110 change from the state provided by the calling function.
111
112 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500113 version_id The version ID whose state change we are
114 waiting for.
115 initial_state The activation state we want to wait for.
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500116 """
117
118 keyword.run_key_u("Open Connection And Log In")
119 retry = 0
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500120 num_read_errors = 0
121 read_fail_threshold = 1
George Keishinge635ddc2022-12-08 07:38:02 -0600122 while (retry < 60):
123 status, software_state = keyword.run_key("Read Properties "
124 + var.SOFTWARE_VERSION_URI
125 + str(version_id),
126 ignore=1)
127 if status == 'FAIL':
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500128 num_read_errors += 1
129 if num_read_errors > read_fail_threshold:
George Keishinge635ddc2022-12-08 07:38:02 -0600130 message = "Read errors exceeds threshold:\n " \
131 + gp.sprint_vars(num_read_errors, read_fail_threshold)
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500132 BuiltIn().fail(message)
Charles Paul Hofer4d26c002017-09-27 08:39:29 -0500133 time.sleep(10)
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500134 continue
135
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500136 current_state = (software_state)["Activation"]
George Keishinge635ddc2022-12-08 07:38:02 -0600137 if (initial_state == current_state):
Charles Paul Hofer4d26c002017-09-27 08:39:29 -0500138 time.sleep(10)
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500139 retry += 1
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500140 num_read_errors = 0
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500141 else:
142 return
143 return
144
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500145
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500146def get_latest_file(dir_path):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500147 r"""
148 Get the path to the latest uploaded file.
149
150 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500151 dir_path Path to the dir from which the name of the
152 last updated file or folder will be
153 returned to the calling function.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500154 """
155
George Keishinge635ddc2022-12-08 07:38:02 -0600156 stdout, stderr, rc = \
157 bsu.bmc_execute_command("cd " + dir_path
158 + "; stat -c '%Y %n' * |"
159 + " sort -k1,1nr | head -n 1")
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500160 return stdout.split(" ")[-1]
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500161
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500162
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500163def get_version_tar(tar_file_path):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500164 r"""
165 Read the image version from the MANIFEST inside the tarball.
166
167 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500168 tar_file_path The path to a tar file that holds the image
169 version inside the MANIFEST.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500170 """
171
Sushil Singh3c77d782019-10-14 05:29:42 -0500172 version = ""
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500173 tar = tarfile.open(tar_file_path)
174 for member in tar.getmembers():
George Keishing42ade542020-07-22 09:51:46 -0500175 BuiltIn().log_to_console(member.name)
176 if member.name != "MANIFEST":
177 continue
Gunnar Mills096cd562018-03-26 10:19:12 -0500178 f = tar.extractfile(member)
179 content = f.read()
George Keishing36efbc02018-12-12 10:18:23 -0600180 if content.find(b"version=") == -1:
181 # This tar member does not contain the version.
182 continue
George Keishing42ade542020-07-22 09:51:46 -0500183 content = content.decode("utf-8", "ignore").split("\n")
George Keishing36efbc02018-12-12 10:18:23 -0600184 content = [x for x in content if "version=" in x]
185 version = content[0].split("=")[-1]
186 break
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500187 tar.close()
188 return version
189
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500190
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500191def get_image_version(file_path):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500192 r"""
193 Read the file for a version object.
194
195 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500196 file_path The path to a file that holds the image
197 version.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500198 """
199
George Keishinge635ddc2022-12-08 07:38:02 -0600200 stdout, stderr, rc = \
201 bsu.bmc_execute_command("cat " + file_path
202 + " | grep \"version=\"", ignore_err=1)
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500203 return (stdout.split("\n")[0]).split("=")[-1]
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500204
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500205
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500206def get_image_purpose(file_path):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500207 r"""
208 Read the file for a purpose object.
209
210 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500211 file_path The path to a file that holds the image
212 purpose.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500213 """
214
George Keishinge635ddc2022-12-08 07:38:02 -0600215 stdout, stderr, rc = \
216 bsu.bmc_execute_command("cat " + file_path
217 + " | grep \"purpose=\"", ignore_err=1)
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500218 return stdout.split("=")[-1]
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500219
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500220
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500221def get_image_path(image_version):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500222 r"""
223 Query the upload image dir for the presence of image matching
224 the version that was read from the MANIFEST before uploading
225 the image. Based on the purpose verify the activation object
226 exists and is either READY or INVALID.
227
228 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500229 image_version The version of the image that should match
230 one of the images in the upload dir.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500231 """
232
George Keishinge635ddc2022-12-08 07:38:02 -0600233 stdout, stderr, rc = \
234 bsu.bmc_execute_command("ls -d " + var.IMAGE_UPLOAD_DIR_PATH + "*/")
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500235
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500236 image_list = stdout.split("\n")
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500237 retry = 0
George Keishinge635ddc2022-12-08 07:38:02 -0600238 while (retry < 10):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500239 for i in range(0, len(image_list)):
240 version = get_image_version(image_list[i] + "MANIFEST")
George Keishinge635ddc2022-12-08 07:38:02 -0600241 if (version == image_version):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500242 return image_list[i]
243 time.sleep(10)
244 retry += 1
245
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500246
George Keishinge635ddc2022-12-08 07:38:02 -0600247def verify_image_upload(image_version,
248 timeout=3):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500249 r"""
250 Verify the image was uploaded correctly and that it created
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500251 a valid d-bus object. If the first check for the image
252 fails, try again until we reach the timeout.
253
254 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500255 image_version The version from the image's manifest file
256 (e.g. "v2.2-253-g00050f1").
257 timeout How long, in minutes, to keep trying to
258 find the image on the BMC. Default is 3 minutes.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500259 """
260
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500261 image_path = get_image_path(image_version)
262 image_version_id = image_path.split("/")[-2]
263
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500264 keyword.run_key_u("Open Connection And Log In")
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500265 image_purpose = get_image_purpose(image_path + "MANIFEST")
George Keishinge635ddc2022-12-08 07:38:02 -0600266 if (image_purpose == var.VERSION_PURPOSE_BMC
267 or image_purpose == var.VERSION_PURPOSE_HOST):
George Keishingff1e3ec2017-07-20 01:58:21 -0500268 uri = var.SOFTWARE_VERSION_URI + image_version_id
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500269 ret_values = ""
270 for itr in range(timeout * 2):
George Keishinge635ddc2022-12-08 07:38:02 -0600271 status, ret_values = \
272 keyword.run_key("Read Attribute " + uri + " Activation")
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500273
George Keishinge635ddc2022-12-08 07:38:02 -0600274 if ((ret_values == var.READY) or (ret_values == var.INVALID)
275 or (ret_values == var.ACTIVE)):
Charles Paul Hofercef61992017-08-18 10:14:18 -0500276 return True, image_version_id
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500277 else:
278 time.sleep(30)
279
280 # If we exit the for loop, the timeout has been reached
281 gp.print_var(ret_values)
Charles Paul Hofercef61992017-08-18 10:14:18 -0500282 return False, None
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500283 else:
Saqib Khanbb8b63f2017-05-24 10:58:01 -0500284 gp.print_var(image_purpose)
Charles Paul Hofercef61992017-08-18 10:14:18 -0500285 return False, None
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500286
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500287
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500288def verify_image_not_in_bmc_uploads_dir(image_version, timeout=3):
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500289 r"""
290 Check that an image with the given version is not unpacked inside of the
291 BMCs image uploads directory. If no image is found, retry every 30 seconds
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500292 until the given timeout is hit, in case the BMC takes time
293 unpacking the image.
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500294
295 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500296 image_version The version of the image to look for on
297 the BMC.
298 timeout How long, in minutes, to try to find an
299 image on the BMC. Default is 3 minutes.
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500300 """
301
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500302 for i in range(timeout * 2):
George Keishinge635ddc2022-12-08 07:38:02 -0600303 stdout, stderr, rc = \
304 bsu.bmc_execute_command('ls ' + var.IMAGE_UPLOAD_DIR_PATH
305 + '*/MANIFEST 2>/dev/null '
306 + '| xargs grep -rl "version='
307 + image_version + '"')
308 image_dir = os.path.dirname(stdout.split('\n')[0])
309 if '' != image_dir:
310 bsu.bmc_execute_command('rm -rf ' + image_dir)
311 BuiltIn().fail('Found invalid BMC Image: ' + image_dir)
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500312 time.sleep(30)