blob: 948cedb871cae5035ea4b3e841d0be3067ff9841 [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):
29 image_purpose The purpose that images must have to be checked for
30 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"
Gunnar Mills096cd562018-03-26 10:19:12 -050044 + gp.sprint_vars(image, taken_priorities[image_priority]))
Charles Paul Hofera5673162017-08-30 09:49:16 -050045 taken_priorities[image_priority] = image
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050046
Charles Paul Hoferc1fa2bc2017-08-18 16:44:03 -050047
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050048def get_non_running_bmc_software_object():
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050049 r"""
50 Get the URI to a BMC image from software that is not running on the BMC.
51 """
52
53 # Get the version of the image currently running on the BMC.
54 _, cur_img_version = keyword.run_key("Get BMC Version")
55 # Remove the surrounding double quotes from the version.
56 cur_img_version = cur_img_version.replace('"', '')
57
58 _, images = keyword.run_key("Read Properties "
59 + var.SOFTWARE_VERSION_URI + "enumerate")
60
61 for image_name in images:
62 _, image_properties = keyword.run_key(
Gunnar Mills096cd562018-03-26 10:19:12 -050063 "Get Host Software Property " + image_name)
Charles Paul Hofer1e487272017-09-14 12:55:11 -050064 if 'Purpose' in image_properties and 'Version' in image_properties \
65 and image_properties['Purpose'] != var.VERSION_PURPOSE_HOST \
Charles Paul Hofer9f74d3a2017-08-18 09:54:28 -050066 and image_properties['Version'] != cur_img_version:
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050067 return image_name
68 BuiltIn().fail("Did not find any non-running BMC images.")
69
Charles Paul Hoferda24d0a2017-08-09 15:03:40 -050070
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050071def delete_all_pnor_images():
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050072 r"""
73 Delete all PNOR images from the BMC.
74 """
75
George Keishing8e984782017-08-30 14:16:18 -050076 status, images = keyword.run_key("Get Software Objects "
77 + var.VERSION_PURPOSE_HOST)
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050078 for image_name in images:
George Keishing8e984782017-08-30 14:16:18 -050079 BuiltIn().log_to_console(image_name)
80 # Delete twice, in case the image is in the /tmp/images directory
81 keyword.run_key("Call Method " + image_name
82 + " delete data={\"data\":[]}")
83 keyword.run_key("Call Method " + image_name
84 + " delete data={\"data\":[]}")
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050085
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050086
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050087def wait_for_activation_state_change(version_id, initial_state):
Charles Paul Hoferde7d4082017-08-08 14:41:01 -050088 r"""
89 Wait for the current activation state of ${version_id} to
90 change from the state provided by the calling function.
91
92 Description of argument(s):
93 version_id The version ID whose state change we are waiting for.
94 initial_state The activation state we want to wait for.
95 """
96
97 keyword.run_key_u("Open Connection And Log In")
98 retry = 0
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -050099 num_read_errors = 0
100 read_fail_threshold = 1
Charles Paul Hofer4d26c002017-09-27 08:39:29 -0500101 while (retry < 30):
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500102 # TODO: Use retry option in run_key when available.
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500103 status, software_state = keyword.run_key("Read Properties " +
Gunnar Mills096cd562018-03-26 10:19:12 -0500104 var.SOFTWARE_VERSION_URI +
105 str(version_id),
106 ignore=1)
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500107 if status == 'FAIL':
108 num_read_errors += 1
109 if num_read_errors > read_fail_threshold:
110 message = "Read errors exceeds threshold:\n " \
Gunnar Mills096cd562018-03-26 10:19:12 -0500111 + gp.sprint_vars(num_read_errors, read_fail_threshold)
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500112 BuiltIn().fail(message)
Charles Paul Hofer4d26c002017-09-27 08:39:29 -0500113 time.sleep(10)
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500114 continue
115
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500116 current_state = (software_state)["Activation"]
117 if (initial_state == current_state):
Charles Paul Hofer4d26c002017-09-27 08:39:29 -0500118 time.sleep(10)
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500119 retry += 1
Charles Paul Hofer290b8bd2017-09-26 10:02:22 -0500120 num_read_errors = 0
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500121 else:
122 return
123 return
124
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500125
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500126def get_latest_file(dir_path):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500127 r"""
128 Get the path to the latest uploaded file.
129
130 Description of argument(s):
131 dir_path Path to the dir from which the name of the last
132 updated file or folder will be returned to the
133 calling function.
134 """
135
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500136 stdout, stderr, rc = \
137 bsu.bmc_execute_command("cd " + dir_path +
138 "; stat -c '%Y %n' * |" +
139 " sort -k1,1nr | head -n 1")
140 return stdout.split(" ")[-1]
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500141
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500142
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500143def get_version_tar(tar_file_path):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500144 r"""
145 Read the image version from the MANIFEST inside the tarball.
146
147 Description of argument(s):
148 tar_file_path The path to a tar file that holds the image
149 version inside the MANIFEST.
150 """
151
152 tar = tarfile.open(tar_file_path)
153 for member in tar.getmembers():
Gunnar Mills096cd562018-03-26 10:19:12 -0500154 f = tar.extractfile(member)
155 content = f.read()
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500156 if "version=" in content:
157 content = content.split("\n")
158 content = [x for x in content if "version=" in x]
159 version = content[0].split("=")[-1]
160 break
161 tar.close()
162 return version
163
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500164
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500165def get_image_version(file_path):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500166 r"""
167 Read the file for a version object.
168
169 Description of argument(s):
170 file_path The path to a file that holds the image version.
171 """
172
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500173 stdout, stderr, rc = \
174 bsu.bmc_execute_command("cat " + file_path +
175 " | grep \"version=\"", ignore_err=1)
176 return (stdout.split("\n")[0]).split("=")[-1]
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500177
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500178
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500179def get_image_purpose(file_path):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500180 r"""
181 Read the file for a purpose object.
182
183 Description of argument(s):
184 file_path The path to a file that holds the image purpose.
185 """
186
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500187 stdout, stderr, rc = \
188 bsu.bmc_execute_command("cat " + file_path +
189 " | grep \"purpose=\"", ignore_err=1)
190 return stdout.split("=")[-1]
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500191
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500192
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500193def get_image_path(image_version):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500194 r"""
195 Query the upload image dir for the presence of image matching
196 the version that was read from the MANIFEST before uploading
197 the image. Based on the purpose verify the activation object
198 exists and is either READY or INVALID.
199
200 Description of argument(s):
201 image_version The version of the image that should match one
202 of the images in the upload dir.
203 """
204
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500205 stdout, stderr, rc = \
206 bsu.bmc_execute_command("ls -d " + var.IMAGE_UPLOAD_DIR_PATH +
207 "*/")
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500208
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500209 image_list = stdout.split("\n")
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500210 retry = 0
211 while (retry < 10):
212 for i in range(0, len(image_list)):
213 version = get_image_version(image_list[i] + "MANIFEST")
214 if (version == image_version):
215 return image_list[i]
216 time.sleep(10)
217 retry += 1
218
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500219
Charles Paul Hofer9f74d3a2017-08-18 09:54:28 -0500220def verify_image_upload(image_version,
221 timeout=3):
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500222 r"""
223 Verify the image was uploaded correctly and that it created
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500224 a valid d-bus object. If the first check for the image
225 fails, try again until we reach the timeout.
226
227 Description of argument(s):
Charles Paul Hofer9f74d3a2017-08-18 09:54:28 -0500228 image_version The version from the image's manifest file
229 (e.g. "IBM-witherspoon-redbud-ibm-OP9_v1.17_1.68").
230 timeout How long, in minutes, to keep trying to find the
231 image on the BMC. Default is 3 minutes.
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500232 """
233
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500234 image_path = get_image_path(image_version)
235 image_version_id = image_path.split("/")[-2]
236
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500237 keyword.run_key_u("Open Connection And Log In")
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500238 image_purpose = get_image_purpose(image_path + "MANIFEST")
Saqib Khanbb8b63f2017-05-24 10:58:01 -0500239 if (image_purpose == var.VERSION_PURPOSE_BMC or
Gunnar Mills096cd562018-03-26 10:19:12 -0500240 image_purpose == var.VERSION_PURPOSE_HOST):
George Keishingff1e3ec2017-07-20 01:58:21 -0500241 uri = var.SOFTWARE_VERSION_URI + image_version_id
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500242 ret_values = ""
243 for itr in range(timeout * 2):
244 status, ret_values = \
Charles Paul Hoferde7d4082017-08-08 14:41:01 -0500245 keyword.run_key("Read Attribute " + uri + " Activation")
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500246
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500247 if ((ret_values == var.READY) or (ret_values == var.INVALID)
248 or (ret_values == var.ACTIVE)):
Charles Paul Hofercef61992017-08-18 10:14:18 -0500249 return True, image_version_id
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500250 else:
251 time.sleep(30)
252
253 # If we exit the for loop, the timeout has been reached
254 gp.print_var(ret_values)
Charles Paul Hofercef61992017-08-18 10:14:18 -0500255 return False, None
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500256 else:
Saqib Khanbb8b63f2017-05-24 10:58:01 -0500257 gp.print_var(image_purpose)
Charles Paul Hofercef61992017-08-18 10:14:18 -0500258 return False, None
Saqib Khanfb1f6ae2017-04-26 13:24:41 -0500259
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500260
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500261def verify_image_not_in_bmc_uploads_dir(image_version, timeout=3):
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500262 r"""
263 Check that an image with the given version is not unpacked inside of the
264 BMCs image uploads directory. If no image is found, retry every 30 seconds
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500265 until the given timeout is hit, in case the BMC takes time
266 unpacking the image.
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500267
268 Description of argument(s):
269 image_version The version of the image to look for on the BMC.
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500270 timeout How long, in minutes, to try to find an image on the BMC.
271 Default is 3 minutes.
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500272 """
273
Charles Paul Hofer6b972682017-07-20 11:36:56 -0500274 for i in range(timeout * 2):
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500275 stdout, stderr, rc = \
276 bsu.bmc_execute_command('ls ' + var.IMAGE_UPLOAD_DIR_PATH +
277 '*/MANIFEST 2>/dev/null ' +
278 '| xargs grep -rl "version=' +
279 image_version + '"')
280 image_dir = os.path.dirname(stdout.split('\n')[0])
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500281 if '' != image_dir:
Joy Onyerikwu9b668972018-05-22 19:10:43 -0500282 bsu.bmc_execute_command('rm -rf ' + image_dir)
Charles P. Hofer1d20acd2017-07-05 15:24:40 -0500283 BuiltIn().fail('Found invalid BMC Image: ' + image_dir)
284 time.sleep(30)