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    num_read_errors = 0
112    read_fail_threshold = 1
113    while (retry < 30):
114        # TODO: Use retry option in run_key when available.
115        status, software_state = keyword.run_key("Read Properties  " +
116                                    var.SOFTWARE_VERSION_URI + str(version_id),
117                                    ignore=1)
118        if status == 'FAIL':
119            num_read_errors += 1
120            if num_read_errors > read_fail_threshold:
121                message = "Read errors exceeds threshold:\n " \
122                        + gp.sprint_vars(num_read_errors, read_fail_threshold)
123                BuiltIn().fail(message)
124            time.sleep(10)
125            continue
126
127        current_state = (software_state)["Activation"]
128        if (initial_state == current_state):
129            time.sleep(10)
130            retry += 1
131            num_read_errors = 0
132        else:
133            return
134    return
135
136###############################################################################
137
138
139###############################################################################
140def get_latest_file(dir_path):
141
142    r"""
143    Get the path to the latest uploaded file.
144
145    Description of argument(s):
146    dir_path    Path to the dir from which the name of the last
147                updated file or folder will be returned to the
148                calling function.
149    """
150
151    keyword.run_key_u("Open Connection And Log In")
152    status, ret_values =\
153            keyword.run_key("Execute Command On BMC  cd " + dir_path
154            + "; stat -c '%Y %n' * | sort -k1,1nr | head -n 1", ignore=1)
155    return ret_values.split(" ")[-1]
156
157###############################################################################
158
159
160###############################################################################
161def get_version_tar(tar_file_path):
162
163    r"""
164    Read the image version from the MANIFEST inside the tarball.
165
166    Description of argument(s):
167    tar_file_path    The path to a tar file that holds the image
168                     version inside the MANIFEST.
169    """
170
171    tar = tarfile.open(tar_file_path)
172    for member in tar.getmembers():
173        f=tar.extractfile(member)
174        content=f.read()
175        if "version=" in content:
176            content = content.split("\n")
177            content = [x for x in content if "version=" in x]
178            version = content[0].split("=")[-1]
179            break
180    tar.close()
181    return version
182
183###############################################################################
184
185
186###############################################################################
187def get_image_version(file_path):
188
189    r"""
190    Read the file for a version object.
191
192    Description of argument(s):
193    file_path    The path to a file that holds the image version.
194    """
195
196    keyword.run_key_u("Open Connection And Log In")
197    status, ret_values =\
198            keyword.run_key("Execute Command On BMC  cat "
199            + file_path + " | grep \"version=\"", ignore=1)
200    return (ret_values.split("\n")[0]).split("=")[-1]
201
202###############################################################################
203
204
205###############################################################################
206def get_image_purpose(file_path):
207
208    r"""
209    Read the file for a purpose object.
210
211    Description of argument(s):
212    file_path    The path to a file that holds the image purpose.
213    """
214
215    keyword.run_key_u("Open Connection And Log In")
216    status, ret_values =\
217            keyword.run_key("Execute Command On BMC  cat "
218            + file_path + " | grep \"purpose=\"", ignore=1)
219    return ret_values.split("=")[-1]
220
221###############################################################################
222
223
224###############################################################################
225def get_image_path(image_version):
226
227    r"""
228    Query the upload image dir for the presence of image matching
229    the version that was read from the MANIFEST before uploading
230    the image. Based on the purpose verify the activation object
231    exists and is either READY or INVALID.
232
233    Description of argument(s):
234    image_version    The version of the image that should match one
235                     of the images in the upload dir.
236    """
237
238    keyword.run_key_u("Open Connection And Log In")
239    status, image_list =\
240            keyword.run_key("Execute Command On BMC  ls -d "
241                            + var.IMAGE_UPLOAD_DIR_PATH + "*/")
242
243    image_list = image_list.split("\n")
244    retry = 0
245    while (retry < 10):
246        for i in range(0, len(image_list)):
247            version = get_image_version(image_list[i] + "MANIFEST")
248            if (version == image_version):
249                return image_list[i]
250        time.sleep(10)
251        retry += 1
252
253###############################################################################
254
255
256###############################################################################
257def verify_image_upload(image_version,
258                        timeout=3):
259
260    r"""
261    Verify the image was uploaded correctly and that it created
262    a valid d-bus object. If the first check for the image
263    fails, try again until we reach the timeout.
264
265    Description of argument(s):
266    image_version  The version from the image's manifest file
267                   (e.g. "IBM-witherspoon-redbud-ibm-OP9_v1.17_1.68").
268    timeout        How long, in minutes, to keep trying to find the
269                   image on the BMC. Default is 3 minutes.
270    """
271
272    image_path = get_image_path(image_version)
273    image_version_id = image_path.split("/")[-2]
274
275    keyword.run_key_u("Open Connection And Log In")
276    image_purpose = get_image_purpose(image_path + "MANIFEST")
277    if (image_purpose == var.VERSION_PURPOSE_BMC or
278        image_purpose == var.VERSION_PURPOSE_HOST):
279        uri = var.SOFTWARE_VERSION_URI + image_version_id
280        ret_values = ""
281        for itr in range(timeout * 2):
282            status, ret_values = \
283                keyword.run_key("Read Attribute  " + uri + "  Activation")
284
285            if ((ret_values == var.READY) or (ret_values == var.INVALID)
286                    or (ret_values == var.ACTIVE)):
287                return True, image_version_id
288            else:
289                time.sleep(30)
290
291        # If we exit the for loop, the timeout has been reached
292        gp.print_var(ret_values)
293        return False, None
294    else:
295        gp.print_var(image_purpose)
296        return False, None
297
298###############################################################################
299
300
301###############################################################################
302def verify_image_not_in_bmc_uploads_dir(image_version, timeout=3):
303
304    r"""
305    Check that an image with the given version is not unpacked inside of the
306    BMCs image uploads directory. If no image is found, retry every 30 seconds
307    until the given timeout is hit, in case the BMC takes time
308    unpacking the image.
309
310    Description of argument(s):
311    image_version  The version of the image to look for on the BMC.
312    timeout        How long, in minutes, to try to find an image on the BMC.
313                   Default is 3 minutes.
314    """
315
316    keyword.run_key('Open Connection And Log In')
317    for i in range(timeout * 2):
318        stat, grep_res = keyword.run_key('Execute Command On BMC  '
319                + 'ls ' + var.IMAGE_UPLOAD_DIR_PATH + '*/MANIFEST 2>/dev/null '
320                + '| xargs grep -rl "version=' + image_version + '"')
321        image_dir = os.path.dirname(grep_res.split('\n')[0])
322        if '' != image_dir:
323            keyword.run_key('Execute Command On BMC  rm -rf ' + image_dir)
324            BuiltIn().fail('Found invalid BMC Image: ' + image_dir)
325        time.sleep(30)
326
327###############################################################################
328