aboutsummaryrefslogtreecommitdiff
path: root/crosperf/download_images.py
blob: 9a46280df25bbca89e612c74f3d22b98c5d8e787 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
# -*- coding: utf-8 -*-
# Copyright 2014-2015 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Download images from Cloud Storage."""


import ast
import os

from cros_utils import command_executer
import test_flag


GS_UTIL = "src/chromium/depot_tools/gsutil.py"


class MissingImage(Exception):
    """Raised when the requested image does not exist in gs://"""


class MissingFile(Exception):
    """Raised when the requested file does not exist in gs://"""


class RunCommandExceptionHandler(object):
    """Handle Exceptions from calls to RunCommand"""

    def __init__(self, logger_to_use, log_level, cmd_exec, command):
        self.logger = logger_to_use
        self.log_level = log_level
        self.ce = cmd_exec
        self.cleanup_command = command

    def HandleException(self, _, e):
        # Exception handler, Run specified command
        if self.log_level != "verbose" and self.cleanup_command is not None:
            self.logger.LogOutput("CMD: %s" % self.cleanup_command)
        if self.cleanup_command is not None:
            _ = self.ce.RunCommand(self.cleanup_command)
        # Raise exception again
        raise e


class ImageDownloader(object):
    """Download images from Cloud Storage."""

    def __init__(self, logger_to_use=None, log_level="verbose", cmd_exec=None):
        self._logger = logger_to_use
        self.log_level = log_level
        self._ce = cmd_exec or command_executer.GetCommandExecuter(
            self._logger, log_level=self.log_level
        )

    def GetBuildID(self, chromeos_root, xbuddy_label):
        # Get the translation of the xbuddy_label into the real Google Storage
        # image name.
        command = (
            "cd /mnt/host/source/src/third_party/toolchain-utils/crosperf; "
            "./translate_xbuddy.py '%s'" % xbuddy_label
        )
        _, build_id_tuple_str, _ = self._ce.ChrootRunCommandWOutput(
            chromeos_root, command
        )
        if not build_id_tuple_str:
            raise MissingImage("Unable to find image for '%s'" % xbuddy_label)

        build_id_tuple = ast.literal_eval(build_id_tuple_str)
        build_id = build_id_tuple[0]

        return build_id

    def DownloadImage(self, chromeos_root, build_id, image_name):
        if self.log_level == "average":
            self._logger.LogOutput(
                "Preparing to download %s image to local "
                "directory." % build_id
            )

        # Make sure the directory for downloading the image exists.
        download_path = os.path.join(chromeos_root, "chroot/tmp", build_id)
        image_path = os.path.join(download_path, "chromiumos_test_image.bin")
        if not os.path.exists(download_path):
            os.makedirs(download_path)

        # Check to see if the image has already been downloaded.  If not,
        # download the image.
        if not os.path.exists(image_path):
            gsutil_cmd = os.path.join(chromeos_root, GS_UTIL)
            command = "%s cp %s %s" % (gsutil_cmd, image_name, download_path)

            if self.log_level != "verbose":
                self._logger.LogOutput("CMD: %s" % command)
            status = self._ce.RunCommand(command)
            downloaded_image_name = os.path.join(
                download_path, "chromiumos_test_image.tar.xz"
            )
            if status != 0 or not os.path.exists(downloaded_image_name):
                raise MissingImage(
                    "Cannot download image: %s." % downloaded_image_name
                )

        return image_path

    def UncompressImage(self, chromeos_root, build_id):
        # Check to see if the file has already been uncompresssed, etc.
        if os.path.exists(
            os.path.join(
                chromeos_root,
                "chroot/tmp",
                build_id,
                "chromiumos_test_image.bin",
            )
        ):
            return

        # Uncompress and untar the downloaded image.
        download_path = os.path.join(chromeos_root, "chroot/tmp", build_id)
        command = (
            "cd %s ; tar -Jxf chromiumos_test_image.tar.xz " % download_path
        )
        # Cleanup command for exception handler
        clean_cmd = "cd %s ; rm -f chromiumos_test_image.bin " % download_path
        exception_handler = RunCommandExceptionHandler(
            self._logger, self.log_level, self._ce, clean_cmd
        )
        if self.log_level != "verbose":
            self._logger.LogOutput("CMD: %s" % command)
            print(
                "(Uncompressing and un-tarring may take a couple of minutes..."
                "please be patient.)"
            )
        retval = self._ce.RunCommand(
            command, except_handler=exception_handler.HandleException
        )
        if retval != 0:
            if self.log_level != "verbose":
                self._logger.LogOutput("CMD: %s" % clean_cmd)
                print("(Removing file chromiumos_test_image.bin.)")
            # Remove partially uncompressed file
            _ = self._ce.RunCommand(clean_cmd)
            # Raise exception for failure to uncompress
            raise MissingImage("Cannot uncompress image: %s." % build_id)

        # Remove compressed image
        command = "cd %s ; rm -f chromiumos_test_image.tar.xz; " % download_path
        if self.log_level != "verbose":
            self._logger.LogOutput("CMD: %s" % command)
            print("(Removing file chromiumos_test_image.tar.xz.)")
        # try removing file, its ok to have an error, print if encountered
        retval = self._ce.RunCommand(command)
        if retval != 0:
            print(
                "(Warning: Could not remove file chromiumos_test_image.tar.xz .)"
            )

    def DownloadSingleFile(self, chromeos_root, build_id, package_file_name):
        # Verify if package files exist
        status = 0
        gs_package_name = "gs://chromeos-image-archive/%s/%s" % (
            build_id,
            package_file_name,
        )
        gsutil_cmd = os.path.join(chromeos_root, GS_UTIL)
        if not test_flag.GetTestMode():
            cmd = "%s ls %s" % (gsutil_cmd, gs_package_name)
            status = self._ce.RunCommand(cmd)
        if status != 0:
            raise MissingFile(
                "Cannot find package file: %s." % package_file_name
            )

        if self.log_level == "average":
            self._logger.LogOutput(
                "Preparing to download %s package to local "
                "directory." % package_file_name
            )

        # Make sure the directory for downloading the package exists.
        download_path = os.path.join(chromeos_root, "chroot/tmp", build_id)
        package_path = os.path.join(download_path, package_file_name)
        if not os.path.exists(download_path):
            os.makedirs(download_path)

        # Check to see if the package file has already been downloaded.  If not,
        # download it.
        if not os.path.exists(package_path):
            command = "%s cp %s %s" % (
                gsutil_cmd,
                gs_package_name,
                download_path,
            )

            if self.log_level != "verbose":
                self._logger.LogOutput("CMD: %s" % command)
            status = self._ce.RunCommand(command)
            if status != 0 or not os.path.exists(package_path):
                raise MissingFile(
                    "Cannot download package: %s ." % package_path
                )

    def UncompressSingleFile(
        self, chromeos_root, build_id, package_file_name, uncompress_cmd
    ):
        # Uncompress file
        download_path = os.path.join(chromeos_root, "chroot/tmp", build_id)
        command = "cd %s ; %s %s" % (
            download_path,
            uncompress_cmd,
            package_file_name,
        )

        if self.log_level != "verbose":
            self._logger.LogOutput("CMD: %s" % command)
            print("(Uncompressing file %s .)" % package_file_name)
        retval = self._ce.RunCommand(command)
        if retval != 0:
            raise MissingFile("Cannot uncompress file: %s." % package_file_name)
        # Remove uncompressed downloaded file
        command = "cd %s ; rm -f %s" % (download_path, package_file_name)
        if self.log_level != "verbose":
            self._logger.LogOutput("CMD: %s" % command)
            print("(Removing processed file %s .)" % package_file_name)
        # try removing file, its ok to have an error, print if encountered
        retval = self._ce.RunCommand(command)
        if retval != 0:
            print("(Warning: Could not remove file %s .)" % package_file_name)

    def VerifyFileExists(self, chromeos_root, build_id, package_file):
        # Quickly verify if the files are there
        status = 0
        gs_package_name = "gs://chromeos-image-archive/%s/%s" % (
            build_id,
            package_file,
        )
        gsutil_cmd = os.path.join(chromeos_root, GS_UTIL)
        if not test_flag.GetTestMode():
            cmd = "%s ls %s" % (gsutil_cmd, gs_package_name)
            if self.log_level != "verbose":
                self._logger.LogOutput("CMD: %s" % cmd)
            status = self._ce.RunCommand(cmd)
            if status != 0:
                print("(Warning: Could not find file %s )" % gs_package_name)
                return 1
        # Package exists on server
        return 0

    def DownloadAutotestFiles(self, chromeos_root, build_id):
        # Download autest package files (3 files)
        autotest_packages_name = "autotest_packages.tar"
        autotest_server_package_name = "autotest_server_package.tar.bz2"
        autotest_control_files_name = "control_files.tar"

        download_path = os.path.join(chromeos_root, "chroot/tmp", build_id)
        # Autotest directory relative path wrt chroot
        autotest_rel_path = os.path.join("/tmp", build_id, "autotest_files")
        # Absolute Path to download files
        autotest_path = os.path.join(
            chromeos_root, "chroot/tmp", build_id, "autotest_files"
        )

        if not os.path.exists(autotest_path):
            # Quickly verify if the files are present on server
            # If not, just exit with warning
            status = self.VerifyFileExists(
                chromeos_root, build_id, autotest_packages_name
            )
            if status != 0:
                default_autotest_dir = (
                    "/mnt/host/source/src/third_party/autotest/files"
                )
                print(
                    "(Warning: Could not find autotest packages .)\n"
                    "(Warning: Defaulting autotest path to %s ."
                    % default_autotest_dir
                )
                return default_autotest_dir

            # Files exist on server, download and uncompress them
            self.DownloadSingleFile(
                chromeos_root, build_id, autotest_packages_name
            )
            self.DownloadSingleFile(
                chromeos_root, build_id, autotest_server_package_name
            )
            self.DownloadSingleFile(
                chromeos_root, build_id, autotest_control_files_name
            )

            self.UncompressSingleFile(
                chromeos_root, build_id, autotest_packages_name, "tar -xf "
            )
            self.UncompressSingleFile(
                chromeos_root,
                build_id,
                autotest_server_package_name,
                "tar -jxf ",
            )
            self.UncompressSingleFile(
                chromeos_root, build_id, autotest_control_files_name, "tar -xf "
            )
            # Rename created autotest directory to autotest_files
            command = "cd %s ; mv autotest autotest_files" % download_path
            if self.log_level != "verbose":
                self._logger.LogOutput("CMD: %s" % command)
                print("(Moving downloaded autotest files to autotest_files)")
            retval = self._ce.RunCommand(command)
            if retval != 0:
                raise MissingFile("Could not create directory autotest_files")

        return autotest_rel_path

    def DownloadDebugFile(self, chromeos_root, build_id):
        # Download autest package files (3 files)
        debug_archive_name = "debug.tgz"

        download_path = os.path.join(chromeos_root, "chroot/tmp", build_id)
        # Debug directory relative path wrt chroot
        debug_rel_path = os.path.join("/tmp", build_id, "debug_files")
        # Debug path to download files
        debug_path = os.path.join(
            chromeos_root, "chroot/tmp", build_id, "debug_files"
        )

        if not os.path.exists(debug_path):
            # Quickly verify if the file is present on server
            # If not, just exit with warning
            status = self.VerifyFileExists(
                chromeos_root, build_id, debug_archive_name
            )
            if status != 0:
                self._logger.LogOutput(
                    "WARNING: Could not find debug archive on gs"
                )
                return ""

            # File exists on server, download and uncompress it
            self.DownloadSingleFile(chromeos_root, build_id, debug_archive_name)

            self.UncompressSingleFile(
                chromeos_root, build_id, debug_archive_name, "tar -xf "
            )
            # Extract and move debug files into the proper location.
            debug_dir = "debug_files/usr/lib"
            command = "cd %s ; mkdir -p %s; mv debug %s" % (
                download_path,
                debug_dir,
                debug_dir,
            )
            if self.log_level != "verbose":
                self._logger.LogOutput("CMD: %s" % command)
                print("Moving downloaded debug files to %s" % debug_dir)
            retval = self._ce.RunCommand(command)
            if retval != 0:
                raise MissingFile(
                    "Could not create directory %s"
                    % os.path.join(debug_dir, "debug")
                )

        return debug_rel_path

    def Run(
        self,
        chromeos_root,
        xbuddy_label,
        autotest_path,
        debug_path,
        download_debug,
    ):
        build_id = self.GetBuildID(chromeos_root, xbuddy_label)
        image_name = (
            "gs://chromeos-image-archive/%s/chromiumos_test_image.tar.xz"
            % build_id
        )

        # Verify that image exists for build_id, before attempting to
        # download it.
        status = 0
        if not test_flag.GetTestMode():
            gsutil_cmd = os.path.join(chromeos_root, GS_UTIL)
            cmd = "%s ls %s" % (gsutil_cmd, image_name)
            status = self._ce.RunCommand(cmd)
        if status != 0:
            raise MissingImage("Cannot find official image: %s." % image_name)

        image_path = self.DownloadImage(chromeos_root, build_id, image_name)
        self.UncompressImage(chromeos_root, build_id)

        if self.log_level != "quiet":
            self._logger.LogOutput("Using image from %s." % image_path)

        if autotest_path == "":
            autotest_path = self.DownloadAutotestFiles(chromeos_root, build_id)

        if debug_path == "" and download_debug:
            debug_path = self.DownloadDebugFile(chromeos_root, build_id)

        return image_path, autotest_path, debug_path