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