1e7e9171eSGeorge Keishing#!/usr/bin/env python3
2de7d408dSCharles Paul Hofer
3de7d408dSCharles Paul Hoferr"""
4de7d408dSCharles Paul HoferThis module provides utilities for code updates.
5de7d408dSCharles Paul Hofer"""
6de7d408dSCharles Paul Hofer
7*09679890SGeorge Keishingfrom robot.libraries.BuiltIn import BuiltIn
8*09679890SGeorge Keishing
9de7d408dSCharles Paul Hoferimport os
10de7d408dSCharles Paul Hoferimport re
11de7d408dSCharles Paul Hoferimport sys
12de7d408dSCharles Paul Hoferimport tarfile
13de7d408dSCharles Paul Hoferimport time
14e635ddc0SGeorge Keishingimport collections
1537c58c8cSGeorge Keishingfrom robot.libraries.BuiltIn import BuiltIn
16de7d408dSCharles Paul Hofer
17de7d408dSCharles Paul Hoferrobot_pgm_dir_path = os.path.dirname(__file__) + os.sep
18e635ddc0SGeorge Keishingrepo_data_path = re.sub('/lib', '/data', robot_pgm_dir_path)
19de7d408dSCharles Paul Hofersys.path.append(repo_data_path)
20de7d408dSCharles Paul Hofer
21*09679890SGeorge Keishingimport bmc_ssh_utils as bsu             # NOQA
22*09679890SGeorge Keishingimport gen_robot_keyword as keyword     # NOQA
23*09679890SGeorge Keishingimport gen_print as gp                  # NOQA
24*09679890SGeorge Keishingimport variables as var                 # NOQA
2537c58c8cSGeorge Keishing
26c1fa2bc9SCharles Paul Hofer
278469a485SSushil Singhdef get_bmc_firmware(image_type, sw_dict):
288469a485SSushil Singh    r"""
298469a485SSushil Singh    Get the dictionary of image based on image type like either BMC or Host.
308469a485SSushil Singh
318469a485SSushil Singh    Description of argument(s):
328469a485SSushil Singh    image_type                     This value is either BMC update or Host update type.
3316b3c7bfSGeorge Keishing    sw_dict                        This contain dictionary of firmware inventory properties.
348469a485SSushil Singh    """
358469a485SSushil Singh
368469a485SSushil Singh    temp_dict = collections.OrderedDict()
378469a485SSushil Singh    for key, value in sw_dict.items():
38e635ddc0SGeorge Keishing        if value['image_type'] == image_type:
398469a485SSushil Singh            temp_dict[key] = value
408469a485SSushil Singh        else:
418469a485SSushil Singh            pass
428469a485SSushil Singh    return temp_dict
438469a485SSushil Singh
448469a485SSushil Singh
45096cd565SGunnar Millsdef verify_no_duplicate_image_priorities(image_purpose):
46c1fa2bc9SCharles Paul Hofer    r"""
47c1fa2bc9SCharles Paul Hofer    Check that there are no active images with the same purpose and priority.
48c1fa2bc9SCharles Paul Hofer
49c1fa2bc9SCharles Paul Hofer    Description of argument(s):
50004ad3c9SJoy Onyerikwu    image_purpose                   The purpose that images must have to be
51004ad3c9SJoy Onyerikwu                                    checked for priority duplicates.
52c1fa2bc9SCharles Paul Hofer    """
53c1fa2bc9SCharles Paul Hofer
54c1fa2bc9SCharles Paul Hofer    taken_priorities = {}
55e635ddc0SGeorge Keishing    _, image_names = keyword.run_key("Get Software Objects  "
56e635ddc0SGeorge Keishing                                     + "version_type=" + image_purpose)
57c1fa2bc9SCharles Paul Hofer
58c1fa2bc9SCharles Paul Hofer    for image_name in image_names:
59c1fa2bc9SCharles Paul Hofer        _, image = keyword.run_key("Get Host Software Property  " + image_name)
60c1fa2bc9SCharles Paul Hofer        if image["Activation"] != var.ACTIVE:
61c1fa2bc9SCharles Paul Hofer            continue
62c1fa2bc9SCharles Paul Hofer        image_priority = image["Priority"]
63c1fa2bc9SCharles Paul Hofer        if image_priority in taken_priorities:
64e635ddc0SGeorge Keishing            BuiltIn().fail("Found active images with the same priority.\n"
65e635ddc0SGeorge Keishing                           + gp.sprint_vars(image,
66e635ddc0SGeorge Keishing                                            taken_priorities[image_priority]))
67c1fa2bc9SCharles Paul Hofer        taken_priorities[image_priority] = image
68c1fa2bc9SCharles Paul Hofer
69c1fa2bc9SCharles Paul Hofer
70da24d0a0SCharles Paul Hoferdef get_non_running_bmc_software_object():
71da24d0a0SCharles Paul Hofer    r"""
72da24d0a0SCharles Paul Hofer    Get the URI to a BMC image from software that is not running on the BMC.
73da24d0a0SCharles Paul Hofer    """
74da24d0a0SCharles Paul Hofer
75da24d0a0SCharles Paul Hofer    # Get the version of the image currently running on the BMC.
76da24d0a0SCharles Paul Hofer    _, cur_img_version = keyword.run_key("Get BMC Version")
77da24d0a0SCharles Paul Hofer    # Remove the surrounding double quotes from the version.
78e635ddc0SGeorge Keishing    cur_img_version = cur_img_version.replace('"', '')
79da24d0a0SCharles Paul Hofer
80e635ddc0SGeorge Keishing    _, images = keyword.run_key("Read Properties  "
81e635ddc0SGeorge Keishing                                + var.SOFTWARE_VERSION_URI + "enumerate")
82da24d0a0SCharles Paul Hofer
83da24d0a0SCharles Paul Hofer    for image_name in images:
84da24d0a0SCharles Paul Hofer        _, image_properties = keyword.run_key(
85e635ddc0SGeorge Keishing            "Get Host Software Property  " + image_name)
86e635ddc0SGeorge Keishing        if 'Purpose' in image_properties and 'Version' in image_properties \
87e635ddc0SGeorge Keishing                and image_properties['Purpose'] != var.VERSION_PURPOSE_HOST \
88e635ddc0SGeorge Keishing                and image_properties['Version'] != cur_img_version:
89da24d0a0SCharles Paul Hofer            return image_name
90da24d0a0SCharles Paul Hofer    BuiltIn().fail("Did not find any non-running BMC images.")
91da24d0a0SCharles Paul Hofer
92da24d0a0SCharles Paul Hofer
93de7d408dSCharles Paul Hoferdef delete_all_pnor_images():
94de7d408dSCharles Paul Hofer    r"""
95de7d408dSCharles Paul Hofer    Delete all PNOR images from the BMC.
96de7d408dSCharles Paul Hofer    """
97de7d408dSCharles Paul Hofer
987eedb1ddSAdriana Kobylak    keyword.run_key("Initiate Host PowerOff")
997eedb1ddSAdriana Kobylak
100e635ddc0SGeorge Keishing    status, images = keyword.run_key("Get Software Objects  "
101e635ddc0SGeorge Keishing                                     + var.VERSION_PURPOSE_HOST)
102de7d408dSCharles Paul Hofer    for image_name in images:
103e635ddc0SGeorge Keishing        keyword.run_key("Delete Image And Verify  " + image_name + "  "
104e635ddc0SGeorge Keishing                        + var.VERSION_PURPOSE_HOST)
105de7d408dSCharles Paul Hofer
106de7d408dSCharles Paul Hofer
107de7d408dSCharles Paul Hoferdef wait_for_activation_state_change(version_id, initial_state):
108de7d408dSCharles Paul Hofer    r"""
109de7d408dSCharles Paul Hofer    Wait for the current activation state of ${version_id} to
110de7d408dSCharles Paul Hofer    change from the state provided by the calling function.
111de7d408dSCharles Paul Hofer
112de7d408dSCharles Paul Hofer    Description of argument(s):
113004ad3c9SJoy Onyerikwu    version_id                      The version ID whose state change we are
114004ad3c9SJoy Onyerikwu                                    waiting for.
115de7d408dSCharles Paul Hofer    initial_state                   The activation state we want to wait for.
116de7d408dSCharles Paul Hofer    """
117de7d408dSCharles Paul Hofer
118de7d408dSCharles Paul Hofer    keyword.run_key_u("Open Connection And Log In")
119de7d408dSCharles Paul Hofer    retry = 0
120290b8bd2SCharles Paul Hofer    num_read_errors = 0
121290b8bd2SCharles Paul Hofer    read_fail_threshold = 1
122e635ddc0SGeorge Keishing    while (retry < 60):
123e635ddc0SGeorge Keishing        status, software_state = keyword.run_key("Read Properties  "
124e635ddc0SGeorge Keishing                                                 + var.SOFTWARE_VERSION_URI
125e635ddc0SGeorge Keishing                                                 + str(version_id),
126e635ddc0SGeorge Keishing                                                 ignore=1)
127e635ddc0SGeorge Keishing        if status == 'FAIL':
128290b8bd2SCharles Paul Hofer            num_read_errors += 1
129290b8bd2SCharles Paul Hofer            if num_read_errors > read_fail_threshold:
130e635ddc0SGeorge Keishing                message = "Read errors exceeds threshold:\n " \
131e635ddc0SGeorge Keishing                    + gp.sprint_vars(num_read_errors, read_fail_threshold)
132290b8bd2SCharles Paul Hofer                BuiltIn().fail(message)
1334d26c008SCharles Paul Hofer            time.sleep(10)
134290b8bd2SCharles Paul Hofer            continue
135290b8bd2SCharles Paul Hofer
136de7d408dSCharles Paul Hofer        current_state = (software_state)["Activation"]
137e635ddc0SGeorge Keishing        if (initial_state == current_state):
1384d26c008SCharles Paul Hofer            time.sleep(10)
139de7d408dSCharles Paul Hofer            retry += 1
140290b8bd2SCharles Paul Hofer            num_read_errors = 0
141de7d408dSCharles Paul Hofer        else:
142de7d408dSCharles Paul Hofer            return
143de7d408dSCharles Paul Hofer    return
144de7d408dSCharles Paul Hofer
145de7d408dSCharles Paul Hofer
146de7d408dSCharles Paul Hoferdef get_latest_file(dir_path):
147de7d408dSCharles Paul Hofer    r"""
148de7d408dSCharles Paul Hofer    Get the path to the latest uploaded file.
149de7d408dSCharles Paul Hofer
150de7d408dSCharles Paul Hofer    Description of argument(s):
151004ad3c9SJoy Onyerikwu    dir_path                        Path to the dir from which the name of the
152004ad3c9SJoy Onyerikwu                                    last updated file or folder will be
153004ad3c9SJoy Onyerikwu                                    returned to the calling function.
154de7d408dSCharles Paul Hofer    """
155de7d408dSCharles Paul Hofer
156e635ddc0SGeorge Keishing    stdout, stderr, rc = \
157e635ddc0SGeorge Keishing        bsu.bmc_execute_command("cd " + dir_path
158004ad3c9SJoy Onyerikwu                                + "; stat -c '%Y %n' * |"
159e635ddc0SGeorge Keishing                                + " sort -k1,1nr | head -n 1")
1609b66897bSJoy Onyerikwu    return stdout.split(" ")[-1]
161de7d408dSCharles Paul Hofer
162de7d408dSCharles Paul Hofer
163de7d408dSCharles Paul Hoferdef get_version_tar(tar_file_path):
164de7d408dSCharles Paul Hofer    r"""
165de7d408dSCharles Paul Hofer    Read the image version from the MANIFEST inside the tarball.
166de7d408dSCharles Paul Hofer
167de7d408dSCharles Paul Hofer    Description of argument(s):
168de7d408dSCharles Paul Hofer    tar_file_path                   The path to a tar file that holds the image
169de7d408dSCharles Paul Hofer                                    version inside the MANIFEST.
170de7d408dSCharles Paul Hofer    """
171de7d408dSCharles Paul Hofer
1723c77d78fSSushil Singh    version = ""
173de7d408dSCharles Paul Hofer    tar = tarfile.open(tar_file_path)
174de7d408dSCharles Paul Hofer    for member in tar.getmembers():
17542ade549SGeorge Keishing        BuiltIn().log_to_console(member.name)
17642ade549SGeorge Keishing        if member.name != "MANIFEST":
17742ade549SGeorge Keishing            continue
178de7d408dSCharles Paul Hofer        f = tar.extractfile(member)
179de7d408dSCharles Paul Hofer        content = f.read()
18036efbc04SGeorge Keishing        if content.find(b"version=") == -1:
18136efbc04SGeorge Keishing            # This tar member does not contain the version.
18236efbc04SGeorge Keishing            continue
18342ade549SGeorge Keishing        content = content.decode("utf-8", "ignore").split("\n")
184de7d408dSCharles Paul Hofer        content = [x for x in content if "version=" in x]
185de7d408dSCharles Paul Hofer        version = content[0].split("=")[-1]
186de7d408dSCharles Paul Hofer        break
187de7d408dSCharles Paul Hofer    tar.close()
188de7d408dSCharles Paul Hofer    return version
189de7d408dSCharles Paul Hofer
190de7d408dSCharles Paul Hofer
191de7d408dSCharles Paul Hoferdef get_image_version(file_path):
192de7d408dSCharles Paul Hofer    r"""
193de7d408dSCharles Paul Hofer    Read the file for a version object.
194de7d408dSCharles Paul Hofer
195de7d408dSCharles Paul Hofer    Description of argument(s):
196004ad3c9SJoy Onyerikwu    file_path                       The path to a file that holds the image
197004ad3c9SJoy Onyerikwu                                    version.
198de7d408dSCharles Paul Hofer    """
199de7d408dSCharles Paul Hofer
200e635ddc0SGeorge Keishing    stdout, stderr, rc = \
201e635ddc0SGeorge Keishing        bsu.bmc_execute_command("cat " + file_path
202e635ddc0SGeorge Keishing                                + " | grep \"version=\"", ignore_err=1)
2039b66897bSJoy Onyerikwu    return (stdout.split("\n")[0]).split("=")[-1]
204de7d408dSCharles Paul Hofer
205de7d408dSCharles Paul Hofer
206de7d408dSCharles Paul Hoferdef get_image_purpose(file_path):
207de7d408dSCharles Paul Hofer    r"""
208de7d408dSCharles Paul Hofer    Read the file for a purpose object.
209de7d408dSCharles Paul Hofer
210de7d408dSCharles Paul Hofer    Description of argument(s):
211004ad3c9SJoy Onyerikwu    file_path                       The path to a file that holds the image
212004ad3c9SJoy Onyerikwu                                    purpose.
213de7d408dSCharles Paul Hofer    """
214de7d408dSCharles Paul Hofer
215e635ddc0SGeorge Keishing    stdout, stderr, rc = \
216e635ddc0SGeorge Keishing        bsu.bmc_execute_command("cat " + file_path
217e635ddc0SGeorge Keishing                                + " | grep \"purpose=\"", ignore_err=1)
2189b66897bSJoy Onyerikwu    return stdout.split("=")[-1]
219de7d408dSCharles Paul Hofer
220de7d408dSCharles Paul Hofer
221de7d408dSCharles Paul Hoferdef get_image_path(image_version):
222de7d408dSCharles Paul Hofer    r"""
223de7d408dSCharles Paul Hofer    Query the upload image dir for the presence of image matching
224de7d408dSCharles Paul Hofer    the version that was read from the MANIFEST before uploading
225de7d408dSCharles Paul Hofer    the image. Based on the purpose verify the activation object
226de7d408dSCharles Paul Hofer    exists and is either READY or INVALID.
227de7d408dSCharles Paul Hofer
228de7d408dSCharles Paul Hofer    Description of argument(s):
229004ad3c9SJoy Onyerikwu    image_version                   The version of the image that should match
230004ad3c9SJoy Onyerikwu                                    one of the images in the upload dir.
231de7d408dSCharles Paul Hofer    """
232de7d408dSCharles Paul Hofer
233e635ddc0SGeorge Keishing    stdout, stderr, rc = \
234e635ddc0SGeorge Keishing        bsu.bmc_execute_command("ls -d " + var.IMAGE_UPLOAD_DIR_PATH + "*/")
235de7d408dSCharles Paul Hofer
2369b66897bSJoy Onyerikwu    image_list = stdout.split("\n")
237de7d408dSCharles Paul Hofer    retry = 0
238e635ddc0SGeorge Keishing    while (retry < 10):
239de7d408dSCharles Paul Hofer        for i in range(0, len(image_list)):
240de7d408dSCharles Paul Hofer            version = get_image_version(image_list[i] + "MANIFEST")
241e635ddc0SGeorge Keishing            if (version == image_version):
242de7d408dSCharles Paul Hofer                return image_list[i]
243de7d408dSCharles Paul Hofer        time.sleep(10)
244de7d408dSCharles Paul Hofer        retry += 1
245de7d408dSCharles Paul Hofer
246de7d408dSCharles Paul Hofer
247e635ddc0SGeorge Keishingdef verify_image_upload(image_version,
248e635ddc0SGeorge Keishing                        timeout=3):
249de7d408dSCharles Paul Hofer    r"""
250de7d408dSCharles Paul Hofer    Verify the image was uploaded correctly and that it created
251de7d408dSCharles Paul Hofer    a valid d-bus object. If the first check for the image
252de7d408dSCharles Paul Hofer    fails, try again until we reach the timeout.
253de7d408dSCharles Paul Hofer
254de7d408dSCharles Paul Hofer    Description of argument(s):
2559f74d3afSCharles Paul Hofer    image_version                   The version from the image's manifest file
256e0a81289SGeorge Keishing                                    (e.g. "v2.2-253-g00050f1").
257004ad3c9SJoy Onyerikwu    timeout                         How long, in minutes, to keep trying to
258004ad3c9SJoy Onyerikwu                                    find the image on the BMC. Default is 3 minutes.
259de7d408dSCharles Paul Hofer    """
260de7d408dSCharles Paul Hofer
261de7d408dSCharles Paul Hofer    image_path = get_image_path(image_version)
262de7d408dSCharles Paul Hofer    image_version_id = image_path.split("/")[-2]
263de7d408dSCharles Paul Hofer
264de7d408dSCharles Paul Hofer    keyword.run_key_u("Open Connection And Log In")
265de7d408dSCharles Paul Hofer    image_purpose = get_image_purpose(image_path + "MANIFEST")
266e635ddc0SGeorge Keishing    if (image_purpose == var.VERSION_PURPOSE_BMC
267e635ddc0SGeorge Keishing            or image_purpose == var.VERSION_PURPOSE_HOST):
268de7d408dSCharles Paul Hofer        uri = var.SOFTWARE_VERSION_URI + image_version_id
269de7d408dSCharles Paul Hofer        ret_values = ""
270de7d408dSCharles Paul Hofer        for itr in range(timeout * 2):
271e635ddc0SGeorge Keishing            status, ret_values = \
272e635ddc0SGeorge Keishing                keyword.run_key("Read Attribute  " + uri + "  Activation")
273de7d408dSCharles Paul Hofer
274e635ddc0SGeorge Keishing            if ((ret_values == var.READY) or (ret_values == var.INVALID)
275e635ddc0SGeorge Keishing                    or (ret_values == var.ACTIVE)):
276cef6199aSCharles Paul Hofer                return True, image_version_id
277de7d408dSCharles Paul Hofer            else:
278de7d408dSCharles Paul Hofer                time.sleep(30)
279de7d408dSCharles Paul Hofer
280de7d408dSCharles Paul Hofer        # If we exit the for loop, the timeout has been reached
281de7d408dSCharles Paul Hofer        gp.print_var(ret_values)
282cef6199aSCharles Paul Hofer        return False, None
283de7d408dSCharles Paul Hofer    else:
284de7d408dSCharles Paul Hofer        gp.print_var(image_purpose)
285cef6199aSCharles Paul Hofer        return False, None
286de7d408dSCharles Paul Hofer
287de7d408dSCharles Paul Hofer
288de7d408dSCharles Paul Hoferdef verify_image_not_in_bmc_uploads_dir(image_version, timeout=3):
289de7d408dSCharles Paul Hofer    r"""
290de7d408dSCharles Paul Hofer    Check that an image with the given version is not unpacked inside of the
291de7d408dSCharles Paul Hofer    BMCs image uploads directory. If no image is found, retry every 30 seconds
292de7d408dSCharles Paul Hofer    until the given timeout is hit, in case the BMC takes time
293de7d408dSCharles Paul Hofer    unpacking the image.
294de7d408dSCharles Paul Hofer
295de7d408dSCharles Paul Hofer    Description of argument(s):
296004ad3c9SJoy Onyerikwu    image_version                   The version of the image to look for on
297004ad3c9SJoy Onyerikwu                                    the BMC.
298004ad3c9SJoy Onyerikwu    timeout                         How long, in minutes, to try to find an
299004ad3c9SJoy Onyerikwu                                    image on the BMC. Default is 3 minutes.
300de7d408dSCharles Paul Hofer    """
301de7d408dSCharles Paul Hofer
302de7d408dSCharles Paul Hofer    for i in range(timeout * 2):
303e635ddc0SGeorge Keishing        stdout, stderr, rc = \
304e635ddc0SGeorge Keishing            bsu.bmc_execute_command('ls ' + var.IMAGE_UPLOAD_DIR_PATH
305e635ddc0SGeorge Keishing                                    + '*/MANIFEST 2>/dev/null '
306004ad3c9SJoy Onyerikwu                                    + '| xargs grep -rl "version='
307e635ddc0SGeorge Keishing                                    + image_version + '"')
308e635ddc0SGeorge Keishing        image_dir = os.path.dirname(stdout.split('\n')[0])
309e635ddc0SGeorge Keishing        if '' != image_dir:
310e635ddc0SGeorge Keishing            bsu.bmc_execute_command('rm -rf ' + image_dir)
311e635ddc0SGeorge Keishing            BuiltIn().fail('Found invalid BMC Image: ' + image_dir)
312de7d408dSCharles Paul Hofer        time.sleep(30)
313