普通文本  |  240行  |  6.8 KB

# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import errno
import logging
import os
import shutil
import subprocess
import urllib2

from autotest_lib.client.common_lib import global_config

def rm_dir_if_exists(dir_to_remove):
    """
    Removes a directory. Does not fail if the directory does NOT exist.

    @param dir_to_remove: path, directory to be removed.

    """
    try:
        shutil.rmtree(dir_to_remove)
    except OSError as e:
        if e.errno != errno.ENOENT:
            raise


def rm_dirs_if_exist(dirs_to_remove):
    """
    Removes multiple directories. Does not fail if directories do NOT exist.

    @param dirs_to_remove: list of directory paths to be removed.

    """
    for dr in dirs_to_remove:
        rm_dir_if_exists(dr)


def ensure_file_exists(filepath):
    """
    Verifies path given points to an existing file.

    @param filepath: path, path to check.

    @raises IOError if the path given does not point to a valid file.

    """
    error_msg = 'File %s does not exist.' % filepath
    if not os.path.isfile(filepath):
        raise IOError(error_msg)


def ensure_all_files_exist(filepaths):
    """
    Verifies all paths given point to existing files.

    @param filepaths: List of paths to check.

    @raises IOError if given paths do not point to existing files.

    """
    for filepath in filepaths:
        ensure_file_exists(filepath)


def ensure_dir_exists(dirpath):
    """
    Verifies path given points to an existing directory.

    @param dirpath: path, dir to check.

    @raises IOError if path does not point to an existing directory.

    """
    error_msg = 'Directory %s does not exist.' % dirpath
    if not os.path.isdir(dirpath):
        raise IOError(error_msg)


def ensure_all_dirs_exist(dirpaths):
    """
    Verifies all paths given point to existing directories.

    @param dirpaths: list of directory paths to check.

    @raises IOError if given paths do not point to existing directories.

    """
    for dirpath in dirpaths:
        ensure_dir_exists(dirpath)


def make_leaf_dir(dirpath):
    """
    Creates a directory, also creating parent directories if they do not exist.

    @param dirpath: path, directory to create.

    @raises whatever exception raised other than "path already exist".

    """
    try:
        os.makedirs(dirpath)
    except OSError as e:
        if e.errno != errno.EEXIST:
            raise


def make_leaf_dirs(dirpaths):
    """
    Creates multiple directories building all respective parent directories if
    they do not exist.

    @param dirpaths: list of directory paths to create.

    @raises whatever exception raised other than "path already exists".
    """
    for dirpath in dirpaths:
        make_leaf_dir(dirpath)


def download_file(remote_path, local_path):
    """
    Download file from a remote resource.

    @param remote_path: path, complete path to the remote file.
    @param local_path: path, complete path to save downloaded file.

    @raises: urllib2.HTTPError or urlib2.URLError exception. Both with added
            debug information

    """
    client_config = global_config.global_config.get_section_values('CLIENT')
    proxies = {}

    for name, value in client_config.items('CLIENT'):
        if value and name.endswith('_proxy'):
            proxies[name[:-6]] = value

    if proxies:
        proxy_handler = urllib2.ProxyHandler(proxies)
        opener = urllib2.build_opener(proxy_handler)
        urllib2.install_opener(opener)

    # Unlike urllib.urlopen urllib2.urlopen will immediately throw on error
    # If we could not find the file pointed by remote_path we will get an
    # exception, catch the exception to log useful information then re-raise

    try:
        remote_file = urllib2.urlopen(remote_path)

        # Catch exceptions, extract exception properties and then re-raise
        # This helps us with debugging what went wrong quickly as we get to see
        # test_that output immediately

    except urllib2.HTTPError as e:
        e.msg = (("""HTTPError raised while retrieving file %s\n.
                       Http Code = %s.\n. Reason = %s\n. Headers = %s.\n
                       Original Message = %s.\n""")
                 % (remote_path, e.code, e.reason, e.headers, e.msg))
        raise

    except urllib2.URLError as e:
        e.msg = (("""URLError raised while retrieving file %s\n.
                        Reason = %s\n. Original Message = %s\n.""")
                 % (remote_path, e.reason, e.message))
        raise

    with open(local_path, 'wb') as local_file:
        while True:
            block = remote_file.read(128 * 1024)
            if not block:
                break
            local_file.write(block)


def get_directory_size_kibibytes(directory):
    """Calculate the total size of a directory with all its contents.

    @param directory: Path to the directory

    @return Size of the directory in kibibytes.
    """
    cmd = ['du', '-sk', directory]
    process = subprocess.Popen(cmd,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE)
    stdout_data, stderr_data = process.communicate()

    if process.returncode != 0:
        # This function is used for statistics only, if it fails,
        # nothing else should crash.
        logging.warning('Getting size of %s failed. Stderr:', directory)
        logging.warning(stderr_data)
        return 0

    return int(stdout_data.split('\t', 1)[0])


def recursive_path_permission(path):
    """
    Recursively lists a path and its parent's permission.

    For a path, it emits a path_perm string "<path> <permission>" where
    <permission> is an octal representation of the path's st_mode. After that,
    it emits its parent's path_perm string(s) recursively.
    For example, recursive_path_permission('/foo/bar/buz') returns
    ['/foo/bar/buz 0100644', '/foo/bar 040755', '/foo 040755', '/ 040755']

    If |path| is invalid, it returns empty list.
    If |path| does not have parent directory, it returns a list with only its
    own path_perm string.

    @param path: file path.

    @return list of "<path> <permission>" string.
    """
    def path_st_mode(p):
        """
        Gets path's permission.

        @param p: file path.

        @return "<path> <permission>" where <permission> is an octal
        representation of the path's st_mode.
        """
        return '%s %s' % (p, oct(os.stat(p).st_mode))

    if not path or not os.path.exists(path):
        return []
    if path == '/':
        return [path_st_mode(path)]
    dirname, basename = os.path.split(path)
    if not basename:
        return recursive_path_permission(dirname)
    result = [path_st_mode(path)]
    if dirname:
        result.extend(recursive_path_permission(dirname))
    return result