xref: /openbmc/openbmc-test-automation/lib/code_update_utils.py (revision 97ecb270b7e6afb058f8353d7bed4be9e727f9b8)
1#!/usr/bin/env python
2
3r"""
4This module provides utilities for code updates.
5"""
6
7import os
8import re
9import sys
10import tarfile
11import time
12
13robot_pgm_dir_path = os.path.dirname(__file__) + os.sep
14repo_data_path = re.sub('/lib', '/data', robot_pgm_dir_path)
15sys.path.append(repo_data_path)
16
17import gen_robot_keyword as keyword
18import gen_print as gp
19import variables as var
20from robot.libraries.BuiltIn import BuiltIn
21
22###############################################################################
23def verify_no_duplicate_image_priorities(image_purpose):
24
25    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"
44                    + gp.sprint_vars(image, taken_priorities[image_priority]))
45        taken_priorities[image_priority] = image
46
47###############################################################################
48
49
50###############################################################################
51def get_non_running_bmc_software_object():
52
53    r"""
54    Get the URI to a BMC image from software that is not running on the BMC.
55    """
56
57    # Get the version of the image currently running on the BMC.
58    _, cur_img_version = keyword.run_key("Get BMC Version")
59    # Remove the surrounding double quotes from the version.
60    cur_img_version = cur_img_version.replace('"', '')
61
62    _, images = keyword.run_key("Read Properties  "
63                                + var.SOFTWARE_VERSION_URI + "enumerate")
64
65    for image_name in images:
66        _, image_properties = keyword.run_key(
67                "Get Host Software Property  " + image_name)
68        if 'Purpose' in image_properties and 'Version' in image_properties \
69                and image_properties['Purpose'] != var.VERSION_PURPOSE_HOST \
70                and image_properties['Version'] != cur_img_version:
71            return image_name
72    BuiltIn().fail("Did not find any non-running BMC images.")
73
74###############################################################################
75
76
77###############################################################################
78def delete_all_pnor_images():
79
80    r"""
81    Delete all PNOR images from the BMC.
82    """
83
84    status, images = keyword.run_key("Get Software Objects  "
85                                     + var.VERSION_PURPOSE_HOST)
86    for image_name in images:
87        BuiltIn().log_to_console(image_name)
88        # Delete twice, in case the image is in the /tmp/images directory
89        keyword.run_key("Call Method  " + image_name
90                        + "  delete  data={\"data\":[]}")
91        keyword.run_key("Call Method  " + image_name
92                        + "  delete  data={\"data\":[]}")
93
94###############################################################################
95
96
97###############################################################################
98def wait_for_activation_state_change(version_id, initial_state):
99
100    r"""
101    Wait for the current activation state of ${version_id} to
102    change from the state provided by the calling function.
103
104    Description of argument(s):
105    version_id     The version ID whose state change we are waiting for.
106    initial_state  The activation state we want to wait for.
107    """
108
109    keyword.run_key_u("Open Connection And Log In")
110    retry = 0
111    while (retry < 20):
112        status, software_state = keyword.run_key("Read Properties  " +
113                                    var.SOFTWARE_VERSION_URI + str(version_id))
114        current_state = (software_state)["Activation"]
115        if (initial_state == current_state):
116            time.sleep(60)
117            retry += 1
118        else:
119            return
120    return
121
122###############################################################################
123
124
125###############################################################################
126def get_latest_file(dir_path):
127
128    r"""
129    Get the path to the latest uploaded file.
130
131    Description of argument(s):
132    dir_path    Path to the dir from which the name of the last
133                updated file or folder will be returned to the
134                calling function.
135    """
136
137    keyword.run_key_u("Open Connection And Log In")
138    status, ret_values =\
139            keyword.run_key("Execute Command On BMC  cd " + dir_path
140            + "; stat -c '%Y %n' * | sort -k1,1nr | head -n 1", ignore=1)
141    return ret_values.split(" ")[-1]
142
143###############################################################################
144
145
146###############################################################################
147def get_version_tar(tar_file_path):
148
149    r"""
150    Read the image version from the MANIFEST inside the tarball.
151
152    Description of argument(s):
153    tar_file_path    The path to a tar file that holds the image
154                     version inside the MANIFEST.
155    """
156
157    tar = tarfile.open(tar_file_path)
158    for member in tar.getmembers():
159        f=tar.extractfile(member)
160        content=f.read()
161        if "version=" in content:
162            content = content.split("\n")
163            content = [x for x in content if "version=" in x]
164            version = content[0].split("=")[-1]
165            break
166    tar.close()
167    return version
168
169###############################################################################
170
171
172###############################################################################
173def get_image_version(file_path):
174
175    r"""
176    Read the file for a version object.
177
178    Description of argument(s):
179    file_path    The path to a file that holds the image version.
180    """
181
182    keyword.run_key_u("Open Connection And Log In")
183    status, ret_values =\
184            keyword.run_key("Execute Command On BMC  cat "
185            + file_path + " | grep \"version=\"", ignore=1)
186    return (ret_values.split("\n")[0]).split("=")[-1]
187
188###############################################################################
189
190
191###############################################################################
192def get_image_purpose(file_path):
193
194    r"""
195    Read the file for a purpose object.
196
197    Description of argument(s):
198    file_path    The path to a file that holds the image purpose.
199    """
200
201    keyword.run_key_u("Open Connection And Log In")
202    status, ret_values =\
203            keyword.run_key("Execute Command On BMC  cat "
204            + file_path + " | grep \"purpose=\"", ignore=1)
205    return ret_values.split("=")[-1]
206
207###############################################################################
208
209
210###############################################################################
211def get_image_path(image_version):
212
213    r"""
214    Query the upload image dir for the presence of image matching
215    the version that was read from the MANIFEST before uploading
216    the image. Based on the purpose verify the activation object
217    exists and is either READY or INVALID.
218
219    Description of argument(s):
220    image_version    The version of the image that should match one
221                     of the images in the upload dir.
222    """
223
224    keyword.run_key_u("Open Connection And Log In")
225    status, image_list =\
226            keyword.run_key("Execute Command On BMC  ls -d "
227                            + var.IMAGE_UPLOAD_DIR_PATH + "*/")
228
229    image_list = image_list.split("\n")
230    retry = 0
231    while (retry < 10):
232        for i in range(0, len(image_list)):
233            version = get_image_version(image_list[i] + "MANIFEST")
234            if (version == image_version):
235                return image_list[i]
236        time.sleep(10)
237        retry += 1
238
239###############################################################################
240
241
242###############################################################################
243def verify_image_upload(image_version,
244                        timeout=3):
245
246    r"""
247    Verify the image was uploaded correctly and that it created
248    a valid d-bus object. If the first check for the image
249    fails, try again until we reach the timeout.
250
251    Description of argument(s):
252    image_version  The version from the image's manifest file
253                   (e.g. "IBM-witherspoon-redbud-ibm-OP9_v1.17_1.68").
254    timeout        How long, in minutes, to keep trying to find the
255                   image on the BMC. Default is 3 minutes.
256    """
257
258    image_path = get_image_path(image_version)
259    image_version_id = image_path.split("/")[-2]
260
261    keyword.run_key_u("Open Connection And Log In")
262    image_purpose = get_image_purpose(image_path + "MANIFEST")
263    if (image_purpose == var.VERSION_PURPOSE_BMC or
264        image_purpose == var.VERSION_PURPOSE_HOST):
265        uri = var.SOFTWARE_VERSION_URI + image_version_id
266        ret_values = ""
267        for itr in range(timeout * 2):
268            status, ret_values = \
269                keyword.run_key("Read Attribute  " + uri + "  Activation")
270
271            if ((ret_values == var.READY) or (ret_values == var.INVALID)
272                    or (ret_values == var.ACTIVE)):
273                return True, image_version_id
274            else:
275                time.sleep(30)
276
277        # If we exit the for loop, the timeout has been reached
278        gp.print_var(ret_values)
279        return False, None
280    else:
281        gp.print_var(image_purpose)
282        return False, None
283
284###############################################################################
285
286
287###############################################################################
288def verify_image_not_in_bmc_uploads_dir(image_version, timeout=3):
289
290    r"""
291    Check that an image with the given version is not unpacked inside of the
292    BMCs image uploads directory. If no image is found, retry every 30 seconds
293    until the given timeout is hit, in case the BMC takes time
294    unpacking the image.
295
296    Description of argument(s):
297    image_version  The version of the image to look for on the BMC.
298    timeout        How long, in minutes, to try to find an image on the BMC.
299                   Default is 3 minutes.
300    """
301
302    keyword.run_key('Open Connection And Log In')
303    for i in range(timeout * 2):
304        stat, grep_res = keyword.run_key('Execute Command On BMC  '
305                + 'ls ' + var.IMAGE_UPLOAD_DIR_PATH + '*/MANIFEST 2>/dev/null '
306                + '| xargs grep -rl "version=' + image_version + '"')
307        image_dir = os.path.dirname(grep_res.split('\n')[0])
308        if '' != image_dir:
309            keyword.run_key('Execute Command On BMC  rm -rf ' + image_dir)
310            BuiltIn().fail('Found invalid BMC Image: ' + image_dir)
311        time.sleep(30)
312
313###############################################################################
314