blob: 4c244ca05e2203afe1f2f29a390ddc688c2f2d63 [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 Williams20f38712022-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 Williams20f38712022-12-08 06:18:26 -060013
George Keishing37c58c82022-12-08 07:42:54 -060014from robot.libraries.BuiltIn import BuiltIn
Saqib Khanfb1f6ae2017-04-26 13:24:41 -050015
16robot_pgm_dir_path = os.path.dirname(__file__) + os.sep
Patrick Williams20f38712022-12-08 06:18:26 -060017repo_data_path = re.sub("/lib", "/data", robot_pgm_dir_path)
Saqib Khanfb1f6ae2017-04-26 13:24:41 -050018sys.path.append(repo_data_path)
19
Patrick Williams20f38712022-12-08 06:18:26 -060020import bmc_ssh_utils as bsu # NOQA
21import gen_print as gp # NOQA
22import gen_robot_keyword as keyword # NOQA
23import variables as var # NOQA
George Keishing37c58c82022-12-08 07:42:54 -060024
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050025
Sushil Singh8469a482020-07-30 04:19:10 -050026def get_bmc_firmware(image_type, sw_dict):
27 r"""
28 Get the dictionary of image based on image type like either BMC or Host.
29
30 Description of argument(s):
31 image_type This value is either BMC update or Host update type.
George Keishing16b3c7b2021-01-28 09:23:37 -060032 sw_dict This contain dictionary of firmware inventory properties.
Sushil Singh8469a482020-07-30 04:19:10 -050033 """
34
35 temp_dict = collections.OrderedDict()
36 for key, value in sw_dict.items():
Patrick Williams20f38712022-12-08 06:18:26 -060037 if value["image_type"] == image_type:
Sushil Singh8469a482020-07-30 04:19:10 -050038 temp_dict[key] = value
39 else:
40 pass
41 return temp_dict
42
43
Gunnar Mills096cd562018-03-26 10:19:12 -050044def verify_no_duplicate_image_priorities(image_purpose):
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050045 r"""
46 Check that there are no active images with the same purpose and priority.
47
48 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050049 image_purpose The purpose that images must have to be
50 checked for priority duplicates.
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050051 """
52
53 taken_priorities = {}
Patrick Williams20f38712022-12-08 06:18:26 -060054 _, image_names = keyword.run_key(
55 "Get Software Objects " + "version_type=" + image_purpose
56 )
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:
Patrick Williams20f38712022-12-08 06:18:26 -060064 BuiltIn().fail(
65 "Found active images with the same priority.\n"
66 + gp.sprint_vars(image, taken_priorities[image_priority])
67 )
Charles Paul Hofera5673162017-08-30 09:49:16 -050068 taken_priorities[image_priority] = image
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050069
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050070
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050071def get_non_running_bmc_software_object():
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050072 r"""
73 Get the URI to a BMC image from software that is not running on the BMC.
74 """
75
76 # Get the version of the image currently running on the BMC.
77 _, cur_img_version = keyword.run_key("Get BMC Version")
78 # Remove the surrounding double quotes from the version.
Patrick Williams20f38712022-12-08 06:18:26 -060079 cur_img_version = cur_img_version.replace('"', "")
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050080
Patrick Williams20f38712022-12-08 06:18:26 -060081 _, images = keyword.run_key(
82 "Read Properties " + var.SOFTWARE_VERSION_URI + "enumerate"
83 )
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050084
85 for image_name in images:
86 _, image_properties = keyword.run_key(
Patrick Williams20f38712022-12-08 06:18:26 -060087 "Get Host Software Property " + image_name
88 )
89 if (
90 "Purpose" in image_properties
91 and "Version" in image_properties
92 and image_properties["Purpose"] != var.VERSION_PURPOSE_HOST
93 and image_properties["Version"] != cur_img_version
94 ):
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050095 return image_name
96 BuiltIn().fail("Did not find any non-running BMC images.")
97
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050098
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050099def delete_all_pnor_images():
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500100 r"""
101 Delete all PNOR images from the BMC.
102 """
103
Adriana Kobylak7eedb1d2019-08-12 15:24:23 -0500104 keyword.run_key("Initiate Host PowerOff")
105
Patrick Williams20f38712022-12-08 06:18:26 -0600106 status, images = keyword.run_key(
107 "Get Software Objects " + var.VERSION_PURPOSE_HOST
108 )
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500109 for image_name in images:
Patrick Williams20f38712022-12-08 06:18:26 -0600110 keyword.run_key(
111 "Delete Image And Verify "
112 + image_name
113 + " "
114 + var.VERSION_PURPOSE_HOST
115 )
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500116
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500117
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500118def wait_for_activation_state_change(version_id, initial_state):
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500119 r"""
120 Wait for the current activation state of ${version_id} to
121 change from the state provided by the calling function.
122
123 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500124 version_id The version ID whose state change we are
125 waiting for.
126 initial_state The activation state we want to wait for.
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500127 """
128
129 keyword.run_key_u("Open Connection And Log In")
130 retry = 0
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500131 num_read_errors = 0
132 read_fail_threshold = 1
Patrick Williams20f38712022-12-08 06:18:26 -0600133 while retry < 60:
134 status, software_state = keyword.run_key(
135 "Read Properties " + var.SOFTWARE_VERSION_URI + str(version_id),
136 ignore=1,
137 )
138 if status == "FAIL":
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500139 num_read_errors += 1
140 if num_read_errors > read_fail_threshold:
Patrick Williams20f38712022-12-08 06:18:26 -0600141 message = "Read errors exceeds threshold:\n " + gp.sprint_vars(
142 num_read_errors, read_fail_threshold
143 )
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500144 BuiltIn().fail(message)
Charles Paul Hofer4d26c002017-09-27 08:39:29 -0500145 time.sleep(10)
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500146 continue
147
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500148 current_state = (software_state)["Activation"]
Patrick Williams20f38712022-12-08 06:18:26 -0600149 if initial_state == current_state:
Charles Paul Hofer4d26c002017-09-27 08:39:29 -0500150 time.sleep(10)
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500151 retry += 1
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500152 num_read_errors = 0
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500153 else:
154 return
155 return
156
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500157
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500158def get_latest_file(dir_path):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500159 r"""
160 Get the path to the latest uploaded file.
161
162 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500163 dir_path Path to the dir from which the name of the
164 last updated file or folder will be
165 returned to the calling function.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500166 """
167
Patrick Williams20f38712022-12-08 06:18:26 -0600168 stdout, stderr, rc = bsu.bmc_execute_command(
169 "cd "
170 + dir_path
171 + "; stat -c '%Y %n' * |"
172 + " sort -k1,1nr | head -n 1"
173 )
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500174 return stdout.split(" ")[-1]
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500175
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500176
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500177def get_version_tar(tar_file_path):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500178 r"""
179 Read the image version from the MANIFEST inside the tarball.
180
181 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500182 tar_file_path The path to a tar file that holds the image
183 version inside the MANIFEST.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500184 """
185
Sushil Singh3c77d782019-10-14 05:29:42 -0500186 version = ""
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500187 tar = tarfile.open(tar_file_path)
188 for member in tar.getmembers():
George Keishing42ade542020-07-22 09:51:46 -0500189 BuiltIn().log_to_console(member.name)
190 if member.name != "MANIFEST":
191 continue
Gunnar Mills096cd562018-03-26 10:19:12 -0500192 f = tar.extractfile(member)
193 content = f.read()
George Keishing36efbc02018-12-12 10:18:23 -0600194 if content.find(b"version=") == -1:
195 # This tar member does not contain the version.
196 continue
George Keishing42ade542020-07-22 09:51:46 -0500197 content = content.decode("utf-8", "ignore").split("\n")
George Keishing36efbc02018-12-12 10:18:23 -0600198 content = [x for x in content if "version=" in x]
199 version = content[0].split("=")[-1]
200 break
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500201 tar.close()
202 return version
203
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500204
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500205def get_image_version(file_path):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500206 r"""
207 Read the file for a version object.
208
209 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500210 file_path The path to a file that holds the image
211 version.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500212 """
213
Patrick Williams20f38712022-12-08 06:18:26 -0600214 stdout, stderr, rc = bsu.bmc_execute_command(
215 "cat " + file_path + ' | grep "version="', ignore_err=1
216 )
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500217 return (stdout.split("\n")[0]).split("=")[-1]
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500218
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500219
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500220def get_image_purpose(file_path):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500221 r"""
222 Read the file for a purpose object.
223
224 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500225 file_path The path to a file that holds the image
226 purpose.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500227 """
228
Patrick Williams20f38712022-12-08 06:18:26 -0600229 stdout, stderr, rc = bsu.bmc_execute_command(
230 "cat " + file_path + ' | grep "purpose="', ignore_err=1
231 )
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500232 return stdout.split("=")[-1]
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500233
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500234
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500235def get_image_path(image_version):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500236 r"""
237 Query the upload image dir for the presence of image matching
238 the version that was read from the MANIFEST before uploading
239 the image. Based on the purpose verify the activation object
240 exists and is either READY or INVALID.
241
242 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500243 image_version The version of the image that should match
244 one of the images in the upload dir.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500245 """
246
Patrick Williams20f38712022-12-08 06:18:26 -0600247 stdout, stderr, rc = bsu.bmc_execute_command(
248 "ls -d " + var.IMAGE_UPLOAD_DIR_PATH + "*/"
249 )
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500250
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500251 image_list = stdout.split("\n")
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500252 retry = 0
Patrick Williams20f38712022-12-08 06:18:26 -0600253 while retry < 10:
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500254 for i in range(0, len(image_list)):
255 version = get_image_version(image_list[i] + "MANIFEST")
Patrick Williams20f38712022-12-08 06:18:26 -0600256 if version == image_version:
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500257 return image_list[i]
258 time.sleep(10)
259 retry += 1
260
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500261
Patrick Williams20f38712022-12-08 06:18:26 -0600262def verify_image_upload(image_version, timeout=3):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500263 r"""
264 Verify the image was uploaded correctly and that it created
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500265 a valid d-bus object. If the first check for the image
266 fails, try again until we reach the timeout.
267
268 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500269 image_version The version from the image's manifest file
270 (e.g. "v2.2-253-g00050f1").
271 timeout How long, in minutes, to keep trying to
272 find the image on the BMC. Default is 3 minutes.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500273 """
274
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500275 image_path = get_image_path(image_version)
276 image_version_id = image_path.split("/")[-2]
277
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500278 keyword.run_key_u("Open Connection And Log In")
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500279 image_purpose = get_image_purpose(image_path + "MANIFEST")
Patrick Williams20f38712022-12-08 06:18:26 -0600280 if (
281 image_purpose == var.VERSION_PURPOSE_BMC
282 or image_purpose == var.VERSION_PURPOSE_HOST
283 ):
George Keishingff1e3ec2017-07-20 01:58:21 -0500284 uri = var.SOFTWARE_VERSION_URI + image_version_id
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500285 ret_values = ""
286 for itr in range(timeout * 2):
Patrick Williams20f38712022-12-08 06:18:26 -0600287 status, ret_values = keyword.run_key(
288 "Read Attribute " + uri + " Activation"
289 )
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500290
Patrick Williams20f38712022-12-08 06:18:26 -0600291 if (
292 (ret_values == var.READY)
293 or (ret_values == var.INVALID)
294 or (ret_values == var.ACTIVE)
295 ):
Charles Paul Hofercef61992017-08-18 10:14:18 -0500296 return True, image_version_id
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500297 else:
298 time.sleep(30)
299
300 # If we exit the for loop, the timeout has been reached
301 gp.print_var(ret_values)
Charles Paul Hofercef61992017-08-18 10:14:18 -0500302 return False, None
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500303 else:
Saqib Khanbb8b63f2017-05-24 10:58:01 -0500304 gp.print_var(image_purpose)
Charles Paul Hofercef61992017-08-18 10:14:18 -0500305 return False, None
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500306
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500307
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500308def verify_image_not_in_bmc_uploads_dir(image_version, timeout=3):
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500309 r"""
310 Check that an image with the given version is not unpacked inside of the
311 BMCs image uploads directory. If no image is found, retry every 30 seconds
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500312 until the given timeout is hit, in case the BMC takes time
313 unpacking the image.
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500314
315 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500316 image_version The version of the image to look for on
317 the BMC.
318 timeout How long, in minutes, to try to find an
319 image on the BMC. Default is 3 minutes.
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500320 """
321
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500322 for i in range(timeout * 2):
Patrick Williams20f38712022-12-08 06:18:26 -0600323 stdout, stderr, rc = bsu.bmc_execute_command(
324 "ls "
325 + var.IMAGE_UPLOAD_DIR_PATH
326 + "*/MANIFEST 2>/dev/null "
327 + '| xargs grep -rl "version='
328 + image_version
329 + '"'
330 )
331 image_dir = os.path.dirname(stdout.split("\n")[0])
332 if "" != image_dir:
333 bsu.bmc_execute_command("rm -rf " + image_dir)
334 BuiltIn().fail("Found invalid BMC Image: " + image_dir)
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500335 time.sleep(30)