blob: 16487dbfc55453a245cf75c6bfcb72595d97e8f6 [file] [log] [blame]
Saqib Khanfb1f6ae2017-04-26 13:24:41 -05001#!/usr/bin/env python
2
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
7import os
Saqib Khanfb1f6ae2017-04-26 13:24:41 -05008import re
Charles Paul Hoferde7d4082017-08-08 14:41:01 -05009import sys
Saqib Khanfb1f6ae2017-04-26 13:24:41 -050010import tarfile
11import time
12
13robot_pgm_dir_path = os.path.dirname(__file__) + os.sep
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050014repo_data_path = re.sub('/lib', '/data', robot_pgm_dir_path)
Saqib Khanfb1f6ae2017-04-26 13:24:41 -050015sys.path.append(repo_data_path)
16
Joy Onyerikwu9b668972018-05-22 19:10:43 -050017import bmc_ssh_utils as bsu
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050018import gen_robot_keyword as keyword
Saqib Khanfb1f6ae2017-04-26 13:24:41 -050019import gen_print as gp
20import variables as var
21from robot.libraries.BuiltIn import BuiltIn
22
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050023
Gunnar Mills096cd562018-03-26 10:19:12 -050024def verify_no_duplicate_image_priorities(image_purpose):
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050025 r"""
26 Check that there are no active images with the same purpose and priority.
27
28 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050029 image_purpose The purpose that images must have to be
30 checked for priority duplicates.
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050031 """
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"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050044 + gp.sprint_vars(image,
45 taken_priorities[image_priority]))
Charles Paul Hofera5673162017-08-30 09:49:16 -050046 taken_priorities[image_priority] = image
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050047
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050048
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050049def get_non_running_bmc_software_object():
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050050 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(
Gunnar Mills096cd562018-03-26 10:19:12 -050064 "Get Host Software Property " + image_name)
Charles Paul Hofer1e487272017-09-14 12:55:11 -050065 if 'Purpose' in image_properties and 'Version' in image_properties \
66 and image_properties['Purpose'] != var.VERSION_PURPOSE_HOST \
Charles Paul Hofer9f74d3a2017-08-18 09:54:28 -050067 and image_properties['Version'] != cur_img_version:
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050068 return image_name
69 BuiltIn().fail("Did not find any non-running BMC images.")
70
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050071
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050072def delete_all_pnor_images():
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050073 r"""
74 Delete all PNOR images from the BMC.
75 """
76
George Keishing8e984782017-08-30 14:16:18 -050077 status, images = keyword.run_key("Get Software Objects "
78 + var.VERSION_PURPOSE_HOST)
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050079 for image_name in images:
George Keishing8e984782017-08-30 14:16:18 -050080 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\":[]}")
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050086
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050087
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050088def wait_for_activation_state_change(version_id, initial_state):
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050089 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):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050094 version_id The version ID whose state change we are
95 waiting for.
96 initial_state The activation state we want to wait for.
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050097 """
98
99 keyword.run_key_u("Open Connection And Log In")
100 retry = 0
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500101 num_read_errors = 0
102 read_fail_threshold = 1
George Keishing532f6c42018-11-27 09:01:57 -0600103 while (retry < 60):
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500104 # TODO: Use retry option in run_key when available.
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500105 status, software_state = keyword.run_key("Read Properties "
106 + var.SOFTWARE_VERSION_URI
107 + str(version_id),
Gunnar Mills096cd562018-03-26 10:19:12 -0500108 ignore=1)
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500109 if status == 'FAIL':
110 num_read_errors += 1
111 if num_read_errors > read_fail_threshold:
112 message = "Read errors exceeds threshold:\n " \
Gunnar Mills096cd562018-03-26 10:19:12 -0500113 + gp.sprint_vars(num_read_errors, read_fail_threshold)
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500114 BuiltIn().fail(message)
Charles Paul Hofer4d26c002017-09-27 08:39:29 -0500115 time.sleep(10)
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500116 continue
117
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500118 current_state = (software_state)["Activation"]
119 if (initial_state == current_state):
Charles Paul Hofer4d26c002017-09-27 08:39:29 -0500120 time.sleep(10)
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500121 retry += 1
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500122 num_read_errors = 0
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500123 else:
124 return
125 return
126
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500127
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500128def get_latest_file(dir_path):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500129 r"""
130 Get the path to the latest uploaded file.
131
132 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500133 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.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500136 """
137
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500138 stdout, stderr, rc = \
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500139 bsu.bmc_execute_command("cd " + dir_path
140 + "; stat -c '%Y %n' * |"
141 + " sort -k1,1nr | head -n 1")
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500142 return stdout.split(" ")[-1]
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500143
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500144
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500145def get_version_tar(tar_file_path):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500146 r"""
147 Read the image version from the MANIFEST inside the tarball.
148
149 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500150 tar_file_path The path to a tar file that holds the image
151 version inside the MANIFEST.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500152 """
153
154 tar = tarfile.open(tar_file_path)
155 for member in tar.getmembers():
Gunnar Mills096cd562018-03-26 10:19:12 -0500156 f = tar.extractfile(member)
157 content = f.read()
George Keishing36efbc02018-12-12 10:18:23 -0600158 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
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500165 tar.close()
166 return version
167
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500168
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500169def get_image_version(file_path):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500170 r"""
171 Read the file for a version object.
172
173 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500174 file_path The path to a file that holds the image
175 version.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500176 """
177
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500178 stdout, stderr, rc = \
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500179 bsu.bmc_execute_command("cat " + file_path
180 + " | grep \"version=\"", ignore_err=1)
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500181 return (stdout.split("\n")[0]).split("=")[-1]
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500182
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500183
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500184def get_image_purpose(file_path):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500185 r"""
186 Read the file for a purpose object.
187
188 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500189 file_path The path to a file that holds the image
190 purpose.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500191 """
192
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500193 stdout, stderr, rc = \
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500194 bsu.bmc_execute_command("cat " + file_path
195 + " | grep \"purpose=\"", ignore_err=1)
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500196 return stdout.split("=")[-1]
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500197
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500198
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500199def get_image_path(image_version):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500200 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):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500207 image_version The version of the image that should match
208 one of the images in the upload dir.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500209 """
210
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500211 stdout, stderr, rc = \
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500212 bsu.bmc_execute_command("ls -d " + var.IMAGE_UPLOAD_DIR_PATH + "*/")
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500213
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500214 image_list = stdout.split("\n")
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500215 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
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500224
Charles Paul Hofer9f74d3a2017-08-18 09:54:28 -0500225def verify_image_upload(image_version,
226 timeout=3):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500227 r"""
228 Verify the image was uploaded correctly and that it created
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500229 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):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500233 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.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500237 """
238
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500239 image_path = get_image_path(image_version)
240 image_version_id = image_path.split("/")[-2]
241
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500242 keyword.run_key_u("Open Connection And Log In")
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500243 image_purpose = get_image_purpose(image_path + "MANIFEST")
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500244 if (image_purpose == var.VERSION_PURPOSE_BMC
245 or image_purpose == var.VERSION_PURPOSE_HOST):
George Keishingff1e3ec2017-07-20 01:58:21 -0500246 uri = var.SOFTWARE_VERSION_URI + image_version_id
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500247 ret_values = ""
248 for itr in range(timeout * 2):
249 status, ret_values = \
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500250 keyword.run_key("Read Attribute " + uri + " Activation")
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500251
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500252 if ((ret_values == var.READY) or (ret_values == var.INVALID)
253 or (ret_values == var.ACTIVE)):
Charles Paul Hofercef61992017-08-18 10:14:18 -0500254 return True, image_version_id
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500255 else:
256 time.sleep(30)
257
258 # If we exit the for loop, the timeout has been reached
259 gp.print_var(ret_values)
Charles Paul Hofercef61992017-08-18 10:14:18 -0500260 return False, None
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500261 else:
Saqib Khanbb8b63f2017-05-24 10:58:01 -0500262 gp.print_var(image_purpose)
Charles Paul Hofercef61992017-08-18 10:14:18 -0500263 return False, None
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500264
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500265
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500266def verify_image_not_in_bmc_uploads_dir(image_version, timeout=3):
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500267 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
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500270 until the given timeout is hit, in case the BMC takes time
271 unpacking the image.
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500272
273 Description of argument(s):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500274 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.
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500278 """
279
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500280 for i in range(timeout * 2):
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500281 stdout, stderr, rc = \
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500282 bsu.bmc_execute_command('ls ' + var.IMAGE_UPLOAD_DIR_PATH
283 + '*/MANIFEST 2>/dev/null '
284 + '| xargs grep -rl "version='
285 + image_version + '"')
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500286 image_dir = os.path.dirname(stdout.split('\n')[0])
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500287 if '' != image_dir:
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500288 bsu.bmc_execute_command('rm -rf ' + image_dir)
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500289 BuiltIn().fail('Found invalid BMC Image: ' + image_dir)
290 time.sleep(30)