Blame tests/cloud_tests/util.py

Packit Service a04d08
# This file is part of cloud-init. See LICENSE file for license information.
Packit Service a04d08
Packit Service a04d08
"""Utilities for re-use across integration tests."""
Packit Service a04d08
Packit Service a04d08
import base64
Packit Service a04d08
import copy
Packit Service a04d08
import glob
Packit Service a04d08
import os
Packit Service a04d08
import random
Packit Service a04d08
import shlex
Packit Service a04d08
import shutil
Packit Service a04d08
import string
Packit Service a04d08
import subprocess
Packit Service a04d08
import tempfile
Packit Service a04d08
import yaml
Packit Service a04d08
Packit Service a04d08
from cloudinit import util as c_util
Packit Service a04d08
from tests.cloud_tests import LOG
Packit Service a04d08
Packit Service a04d08
OS_FAMILY_MAPPING = {
Packit Service a04d08
    'debian': ['debian', 'ubuntu'],
Packit Service a04d08
    'redhat': ['centos', 'rhel', 'fedora'],
Packit Service a04d08
    'gentoo': ['gentoo'],
Packit Service a04d08
    'freebsd': ['freebsd'],
Packit Service a04d08
    'suse': ['sles'],
Packit Service a04d08
    'arch': ['arch'],
Packit Service a04d08
}
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def list_test_data(data_dir):
Packit Service a04d08
    """Find all tests with test data available in data_dir.
Packit Service a04d08
Packit Service a04d08
    @param data_dir: should contain <platforms>/<os_name>/<testnames>/<data>
Packit Service a04d08
    @return_value: {<platform>: {<os_name>: [<testname>]}}
Packit Service a04d08
    """
Packit Service a04d08
    if not os.path.isdir(data_dir):
Packit Service a04d08
        raise ValueError("bad data dir")
Packit Service a04d08
Packit Service a04d08
    res = {}
Packit Service a04d08
    for platform in os.listdir(data_dir):
Packit Service a04d08
        if not os.path.isdir(os.path.join(data_dir, platform)):
Packit Service a04d08
            continue
Packit Service a04d08
Packit Service a04d08
        res[platform] = {}
Packit Service a04d08
        for os_name in os.listdir(os.path.join(data_dir, platform)):
Packit Service a04d08
            res[platform][os_name] = [
Packit Service a04d08
                os.path.sep.join(f.split(os.path.sep)[-2:]) for f in
Packit Service a04d08
                glob.glob(os.sep.join((data_dir, platform, os_name, '*/*')))]
Packit Service a04d08
Packit Service a04d08
    LOG.debug('found test data: %s\n', res)
Packit Service a04d08
    return res
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def gen_instance_name(prefix='cloud-test', image_desc=None, use_desc=None,
Packit Service a04d08
                      max_len=63, delim='-', max_tries=16, used_list=None,
Packit Service a04d08
                      valid=string.ascii_lowercase + string.digits):
Packit Service a04d08
    """Generate an unique name for a test instance.
Packit Service a04d08
Packit Service a04d08
    @param prefix: name prefix, defaults to cloud-test, default should be left
Packit Service a04d08
    @param image_desc: short string (len <= 16) with image desc
Packit Service a04d08
    @param use_desc: short string (len <= 30) with usage desc
Packit Service a04d08
    @param max_len: maximum name length, defaults to 64 chars
Packit Service a04d08
    @param delim: delimiter to use between tokens
Packit Service a04d08
    @param max_tries: maximum tries to find a unique name before giving up
Packit Service a04d08
    @param used_list: already used names, or none to not check
Packit Service a04d08
    @param valid: string of valid characters for name
Packit Service a04d08
    @return_value: valid, unused name, may raise StopIteration
Packit Service a04d08
    """
Packit Service a04d08
    unknown = 'unknown'
Packit Service a04d08
Packit Service a04d08
    def join(*args):
Packit Service a04d08
        """Join args with delim."""
Packit Service a04d08
        return delim.join(args)
Packit Service a04d08
Packit Service a04d08
    def fill(*args):
Packit Service a04d08
        """Join name elems and fill rest with random data."""
Packit Service a04d08
        name = join(*args)
Packit Service a04d08
        num = max_len - len(name) - len(delim)
Packit Service a04d08
        return join(name, ''.join(random.choice(valid) for _ in range(num)))
Packit Service a04d08
Packit Service a04d08
    def clean(elem, max_len):
Packit Service a04d08
        """Filter bad characters out of elem and trim to length."""
Packit Service a04d08
        elem = elem.lower()[:max_len] if elem else unknown
Packit Service a04d08
        return ''.join(c if c in valid else delim for c in elem)
Packit Service a04d08
Packit Service a04d08
    return next(name for name in
Packit Service a04d08
                (fill(prefix, clean(image_desc, 16), clean(use_desc, 30))
Packit Service a04d08
                 for _ in range(max_tries))
Packit Service a04d08
                if not used_list or name not in used_list)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def sorted_unique(iterable, key=None, reverse=False):
Packit Service a04d08
    """Create unique sorted list.
Packit Service a04d08
Packit Service a04d08
    @param iterable: the data structure to sort
Packit Service a04d08
    @param key: if you have a specific key
Packit Service a04d08
    @param reverse: to reverse or not
Packit Service a04d08
    @return_value: a sorted list of unique items in iterable
Packit Service a04d08
    """
Packit Service a04d08
    return sorted(set(iterable), key=key, reverse=reverse)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def get_os_family(os_name):
Packit Service a04d08
    """Get os family type for os_name.
Packit Service a04d08
Packit Service a04d08
    @param os_name: name of os
Packit Service a04d08
    @return_value: family name for os_name
Packit Service a04d08
    """
Packit Service a04d08
    return next((k for k, v in OS_FAMILY_MAPPING.items()
Packit Service a04d08
                 if os_name.lower() in v), None)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def current_verbosity():
Packit Service a04d08
    """Get verbosity currently in effect from log level.
Packit Service a04d08
Packit Service a04d08
    @return_value: verbosity, 0-2, 2=verbose, 0=quiet
Packit Service a04d08
    """
Packit Service a04d08
    return max(min(3 - int(LOG.level / 10), 2), 0)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def is_writable_dir(path):
Packit Service a04d08
    """Make sure dir is writable.
Packit Service a04d08
Packit Service a04d08
    @param path: path to determine if writable
Packit Service a04d08
    @return_value: boolean with result
Packit Service a04d08
    """
Packit Service a04d08
    try:
Packit Service a04d08
        c_util.ensure_dir(path)
Packit Service a04d08
        os.remove(tempfile.mkstemp(dir=os.path.abspath(path))[1])
Packit Service a04d08
    except (IOError, OSError):
Packit Service a04d08
        return False
Packit Service a04d08
    return True
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def is_clean_writable_dir(path):
Packit Service a04d08
    """Make sure dir is empty and writable, creating it if it does not exist.
Packit Service a04d08
Packit Service a04d08
    @param path: path to check
Packit Service a04d08
    @return_value: True/False if successful
Packit Service a04d08
    """
Packit Service a04d08
    path = os.path.abspath(path)
Packit Service a04d08
    if not (is_writable_dir(path) and len(os.listdir(path)) == 0):
Packit Service a04d08
        return False
Packit Service a04d08
    return True
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def configure_yaml():
Packit Service a04d08
    """Clean yaml."""
Packit Service a04d08
    yaml.add_representer(str, (lambda dumper, data: dumper.represent_scalar(
Packit Service a04d08
        'tag:yaml.org,2002:str', data, style='|' if '\n' in data else '')))
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def yaml_format(data, content_type=None):
Packit Service a04d08
    """Format data as yaml.
Packit Service a04d08
Packit Service a04d08
    @param data: data to dump
Packit Service a04d08
    @param header: if specified, add a header to the dumped data
Packit Service a04d08
    @return_value: yaml string
Packit Service a04d08
    """
Packit Service a04d08
    configure_yaml()
Packit Service a04d08
    content_type = (
Packit Service a04d08
        '#{}\n'.format(content_type.strip('#\n')) if content_type else '')
Packit Service a04d08
    return content_type + yaml.dump(data, indent=2, default_flow_style=False)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def yaml_dump(data, path):
Packit Service a04d08
    """Dump data to path in yaml format."""
Packit Service a04d08
    c_util.write_file(os.path.abspath(path), yaml_format(data), omode='w')
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def merge_results(data, path):
Packit Service a04d08
    """Handle merging results from collect phase and verify phase."""
Packit Service a04d08
    current = {}
Packit Service a04d08
    if os.path.exists(path):
Packit Service a04d08
        with open(path, 'r') as fp:
Packit Service a04d08
            current = c_util.load_yaml(fp.read())
Packit Service a04d08
    current.update(data)
Packit Service a04d08
    yaml_dump(current, path)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def rel_files(basedir):
Packit Service a04d08
    """List of files under directory by relative path, not including dirs.
Packit Service a04d08
Packit Service a04d08
    @param basedir: directory to search
Packit Service a04d08
    @return_value: list or relative paths
Packit Service a04d08
    """
Packit Service a04d08
    basedir = os.path.normpath(basedir)
Packit Service a04d08
    return [path[len(basedir) + 1:] for path in
Packit Service a04d08
            glob.glob(os.path.join(basedir, '**'), recursive=True)
Packit Service a04d08
            if not os.path.isdir(path)]
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def flat_tar(output, basedir, owner='root', group='root'):
Packit Service a04d08
    """Create a flat tar archive (no leading ./) from basedir.
Packit Service a04d08
Packit Service a04d08
    @param output: output tar file to write
Packit Service a04d08
    @param basedir: base directory for archive
Packit Service a04d08
    @param owner: owner of archive files
Packit Service a04d08
    @param group: group archive files belong to
Packit Service a04d08
    @return_value: none
Packit Service a04d08
    """
Packit Service 11b429
    c_util.subp(['tar', 'cf', output, '--owner', owner, '--group', group,
Packit Service 11b429
                 '-C', basedir] + rel_files(basedir), capture=True)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def parse_conf_list(entries, valid=None, boolean=False):
Packit Service a04d08
    """Parse config in a list of strings in key=value format.
Packit Service a04d08
Packit Service a04d08
    @param entries: list of key=value strings
Packit Service a04d08
    @param valid: list of valid keys in result, return None if invalid input
Packit Service a04d08
    @param boolean: if true, then interpret all values as booleans
Packit Service a04d08
    @return_value: dict of configuration or None if invalid
Packit Service a04d08
    """
Packit Service a04d08
    res = {key: value.lower() == 'true' if boolean else value
Packit Service a04d08
           for key, value in (i.split('=') for i in entries)}
Packit Service a04d08
    return res if not valid or all(k in valid for k in res.keys()) else None
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def update_args(args, updates, preserve_old=True):
Packit Service a04d08
    """Update cmdline arguments from a dictionary.
Packit Service a04d08
Packit Service a04d08
    @param args: cmdline arguments
Packit Service a04d08
    @param updates: dictionary of {arg_name: new_value} mappings
Packit Service a04d08
    @param preserve_old: if true, create a deep copy of args before updating
Packit Service a04d08
    @return_value: updated cmdline arguments
Packit Service a04d08
    """
Packit Service a04d08
    args = copy.deepcopy(args) if preserve_old else args
Packit Service a04d08
    if updates:
Packit Service a04d08
        vars(args).update(updates)
Packit Service a04d08
    return args
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def update_user_data(user_data, updates, dump_to_yaml=True):
Packit Service a04d08
    """Update user_data from dictionary.
Packit Service a04d08
Packit Service a04d08
    @param user_data: user data as yaml string or dict
Packit Service a04d08
    @param updates: dictionary to merge with user data
Packit Service a04d08
    @param dump_to_yaml: return as yaml dumped string if true
Packit Service a04d08
    @return_value: updated user data, as yaml string if dump_to_yaml is true
Packit Service a04d08
    """
Packit Service a04d08
    user_data = (c_util.load_yaml(user_data)
Packit Service a04d08
                 if isinstance(user_data, str) else copy.deepcopy(user_data))
Packit Service a04d08
    user_data.update(updates)
Packit Service a04d08
    return (yaml_format(user_data, content_type='cloud-config')
Packit Service a04d08
            if dump_to_yaml else user_data)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def shell_safe(cmd):
Packit Service a04d08
    """Produce string safe shell string.
Packit Service a04d08
Packit Service a04d08
    Create a string that can be passed to:
Packit Service a04d08
         set -- <string>
Packit Service a04d08
    to produce the same array that cmd represents.
Packit Service a04d08
Packit Service a04d08
    Internally we utilize 'getopt's ability/knowledge on how to quote
Packit Service a04d08
    strings to be safe for shell.  This implementation could be changed
Packit Service a04d08
    to be pure python.  It is just a matter of correctly escaping
Packit Service a04d08
    or quoting characters like: ' " ^ & $ ; ( ) ...
Packit Service a04d08
Packit Service a04d08
    @param cmd: command as a list
Packit Service a04d08
    """
Packit Service a04d08
    out = subprocess.check_output(
Packit Service a04d08
        ["getopt", "--shell", "sh", "--options", "", "--", "--"] + list(cmd))
Packit Service a04d08
    # out contains ' -- <data>\n'. drop the ' -- ' and the '\n'
Packit Service a04d08
    return out.decode()[4:-1]
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def shell_pack(cmd):
Packit Service a04d08
    """Return a string that can shuffled through 'sh' and execute cmd.
Packit Service a04d08
Packit Service a04d08
    In Python subprocess terms:
Packit Service a04d08
        check_output(cmd) == check_output(shell_pack(cmd), shell=True)
Packit Service a04d08
Packit Service a04d08
    @param cmd: list or string of command to pack up
Packit Service a04d08
    """
Packit Service a04d08
Packit Service a04d08
    if isinstance(cmd, str):
Packit Service a04d08
        cmd = [cmd]
Packit Service a04d08
    else:
Packit Service a04d08
        cmd = list(cmd)
Packit Service a04d08
Packit Service a04d08
    stuffed = shell_safe(cmd)
Packit Service a04d08
    # for whatever reason b64encode returns bytes when it is clearly
Packit Service a04d08
    # representable as a string by nature of being base64 encoded.
Packit Service a04d08
    b64 = base64.b64encode(stuffed.encode()).decode()
Packit Service a04d08
    return 'eval set -- "$(echo %s | base64 --decode)" && exec "$@"' % b64
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def shell_quote(cmd):
Packit Service a04d08
    if isinstance(cmd, (tuple, list)):
Packit Service a04d08
        return ' '.join([shlex.quote(x) for x in cmd])
Packit Service a04d08
    return shlex.quote(cmd)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
class TargetBase(object):
Packit Service a04d08
    _tmp_count = 0
Packit Service a04d08
Packit Service a04d08
    def execute(self, command, stdin=None, env=None,
Packit Service a04d08
                rcs=None, description=None):
Packit Service a04d08
        """Execute command in instance, recording output, error and exit code.
Packit Service a04d08
Packit Service a04d08
        Assumes functional networking and execution as root with the
Packit Service a04d08
        target filesystem being available at /.
Packit Service a04d08
Packit Service a04d08
        @param command: the command to execute as root inside the image
Packit Service a04d08
            if command is a string, then it will be executed as:
Packit Service a04d08
            ['sh', '-c', command]
Packit Service a04d08
        @param stdin: bytes content for standard in
Packit Service a04d08
        @param env: environment variables
Packit Service a04d08
        @param rcs: return codes.
Packit Service a04d08
                    None (default): non-zero exit code will raise exception.
Packit Service a04d08
                    False: any is allowed (No execption raised).
Packit Service a04d08
                    list of int: any rc not in the list will raise exception.
Packit Service a04d08
        @param description: purpose of command
Packit Service a04d08
        @return_value: tuple containing stdout data, stderr data, exit code
Packit Service a04d08
        """
Packit Service a04d08
        if isinstance(command, str):
Packit Service a04d08
            command = ['sh', '-c', command]
Packit Service a04d08
Packit Service a04d08
        if rcs is None:
Packit Service a04d08
            rcs = (0,)
Packit Service a04d08
Packit Service a04d08
        if description:
Packit Service a04d08
            LOG.debug('executing "%s"', description)
Packit Service a04d08
        else:
Packit Service a04d08
            LOG.debug("executing command: %s", shell_quote(command))
Packit Service a04d08
Packit Service a04d08
        out, err, rc = self._execute(command=command, stdin=stdin, env=env)
Packit Service a04d08
Packit Service a04d08
        # False means accept anything.
Packit Service a04d08
        if (rcs is False or rc in rcs):
Packit Service a04d08
            return out, err, rc
Packit Service a04d08
Packit Service a04d08
        raise InTargetExecuteError(out, err, rc, command, description)
Packit Service a04d08
Packit Service a04d08
    def _execute(self, command, stdin=None, env=None):
Packit Service a04d08
        """Execute command in inside, return stdout, stderr and exit code.
Packit Service a04d08
Packit Service a04d08
        Assumes functional networking and execution as root with the
Packit Service a04d08
        target filesystem being available at /.
Packit Service a04d08
Packit Service a04d08
        @param stdin: bytes content for standard in
Packit Service a04d08
        @param env: environment variables
Packit Service a04d08
        @return_value: tuple containing stdout data, stderr data, exit code
Packit Service a04d08
Packit Service a04d08
        This is intended to be implemented by the Image or Instance.
Packit Service a04d08
        Many callers will use the higher level 'execute'."""
Packit Service a04d08
        raise NotImplementedError("_execute must be implemented by subclass.")
Packit Service a04d08
Packit Service a04d08
    def read_data(self, remote_path, decode=False):
Packit Service a04d08
        """Read data from instance filesystem.
Packit Service a04d08
Packit Service a04d08
        @param remote_path: path in instance
Packit Service a04d08
        @param decode: decode data before returning.
Packit Service a04d08
        @return_value: content of remote_path as bytes if 'decode' is False,
Packit Service a04d08
                       and as string if 'decode' is True.
Packit Service a04d08
        """
Packit Service a04d08
        # when sh is invoked with '-c', then the first argument is "$0"
Packit Service a04d08
        # which is commonly understood as the "program name".
Packit Service a04d08
        # 'read_data' is the program name, and 'remote_path' is '$1'
Packit Service a04d08
        stdout, _stderr, rc = self._execute(
Packit Service a04d08
            ["sh", "-c", 'exec cat "$1"', 'read_data', remote_path])
Packit Service a04d08
        if rc != 0:
Packit Service a04d08
            raise RuntimeError("Failed to read file '%s'" % remote_path)
Packit Service a04d08
Packit Service a04d08
        if decode:
Packit Service a04d08
            return stdout.decode()
Packit Service a04d08
        return stdout
Packit Service a04d08
Packit Service a04d08
    def write_data(self, remote_path, data):
Packit Service a04d08
        """Write data to instance filesystem.
Packit Service a04d08
Packit Service a04d08
        @param remote_path: path in instance
Packit Service a04d08
        @param data: data to write in bytes
Packit Service a04d08
        """
Packit Service a04d08
        # when sh is invoked with '-c', then the first argument is "$0"
Packit Service a04d08
        # which is commonly understood as the "program name".
Packit Service a04d08
        # 'write_data' is the program name, and 'remote_path' is '$1'
Packit Service a04d08
        _, _, rc = self._execute(
Packit Service a04d08
            ["sh", "-c", 'exec cat >"$1"', 'write_data', remote_path],
Packit Service a04d08
            stdin=data)
Packit Service a04d08
Packit Service a04d08
        if rc != 0:
Packit Service a04d08
            raise RuntimeError("Failed to write to '%s'" % remote_path)
Packit Service a04d08
        return
Packit Service a04d08
Packit Service a04d08
    def pull_file(self, remote_path, local_path):
Packit Service a04d08
        """Copy file at 'remote_path', from instance to 'local_path'.
Packit Service a04d08
Packit Service a04d08
        @param remote_path: path on remote instance
Packit Service a04d08
        @param local_path: path on local instance
Packit Service a04d08
        """
Packit Service a04d08
        with open(local_path, 'wb') as fp:
Packit Service a04d08
            fp.write(self.read_data(remote_path))
Packit Service a04d08
Packit Service a04d08
    def push_file(self, local_path, remote_path):
Packit Service a04d08
        """Copy file at 'local_path' to instance at 'remote_path'.
Packit Service a04d08
Packit Service a04d08
        @param local_path: path on local instance
Packit Service a04d08
        @param remote_path: path on remote instance"""
Packit Service a04d08
        with open(local_path, "rb") as fp:
Packit Service a04d08
            self.write_data(remote_path, data=fp.read())
Packit Service a04d08
Packit Service a04d08
    def run_script(self, script, rcs=None, description=None):
Packit Service a04d08
        """Run script in target and return stdout.
Packit Service a04d08
Packit Service a04d08
        @param script: script contents
Packit Service a04d08
        @param rcs: allowed return codes from script
Packit Service a04d08
        @param description: purpose of script
Packit Service a04d08
        @return_value: stdout from script
Packit Service a04d08
        """
Packit Service a04d08
        # Just write to a file, add execute, run it, then remove it.
Packit Service a04d08
        shblob = '; '.join((
Packit Service a04d08
            'set -e',
Packit Service a04d08
            's="$1"',
Packit Service a04d08
            'shift',
Packit Service a04d08
            'cat > "$s"',
Packit Service a04d08
            'trap "rm -f $s" EXIT',
Packit Service a04d08
            'chmod +x "$s"',
Packit Service a04d08
            '"$s" "$@"'))
Packit Service a04d08
        return self.execute(
Packit Service a04d08
            ['sh', '-c', shblob, 'runscript', self.tmpfile()],
Packit Service a04d08
            stdin=script, description=description, rcs=rcs)
Packit Service a04d08
Packit Service a04d08
    def tmpfile(self):
Packit Service a04d08
        """Get a tmp file in the target.
Packit Service a04d08
Packit Service a04d08
        @return_value: path to new file in target
Packit Service a04d08
        """
Packit Service a04d08
        path = "/tmp/%s-%04d" % (type(self).__name__, self._tmp_count)
Packit Service a04d08
        self._tmp_count += 1
Packit Service a04d08
        return path
Packit Service a04d08
Packit Service a04d08
Packit Service 11b429
class InTargetExecuteError(c_util.ProcessExecutionError):
Packit Service a04d08
    """Error type for in target commands that fail."""
Packit Service a04d08
Packit Service a04d08
    default_desc = 'Unexpected error while running command.'
Packit Service a04d08
Packit Service a04d08
    def __init__(self, stdout, stderr, exit_code, cmd, description=None,
Packit Service a04d08
                 reason=None):
Packit Service a04d08
        """Init error and parent error class."""
Packit Service a04d08
        super(InTargetExecuteError, self).__init__(
Packit Service a04d08
            stdout=stdout, stderr=stderr, exit_code=exit_code,
Packit Service a04d08
            cmd=shell_quote(cmd),
Packit Service a04d08
            description=description if description else self.default_desc,
Packit Service a04d08
            reason=reason)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
class PlatformError(IOError):
Packit Service a04d08
    """Error type for platform errors."""
Packit Service a04d08
Packit Service a04d08
    default_desc = 'unexpected error in platform.'
Packit Service a04d08
Packit Service a04d08
    def __init__(self, operation, description=None):
Packit Service a04d08
        """Init error and parent error class."""
Packit Service a04d08
        description = description if description else self.default_desc
Packit Service a04d08
Packit Service a04d08
        message = '%s: %s' % (operation, description)
Packit Service a04d08
        IOError.__init__(self, message)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def mkdtemp(prefix='cloud_test_data'):
Packit Service a04d08
    return tempfile.mkdtemp(prefix=prefix)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
class TempDir(object):
Packit Service a04d08
    """Configurable temporary directory like tempfile.TemporaryDirectory."""
Packit Service a04d08
Packit Service a04d08
    def __init__(self, tmpdir=None, preserve=False, prefix='cloud_test_data_'):
Packit Service a04d08
        """Initialize.
Packit Service a04d08
Packit Service a04d08
        @param tmpdir: directory to use as tempdir
Packit Service a04d08
        @param preserve: if true, always preserve data on exit
Packit Service a04d08
        @param prefix: prefix to use for tempfile name
Packit Service a04d08
        """
Packit Service a04d08
        self.tmpdir = tmpdir
Packit Service a04d08
        self.preserve = preserve
Packit Service a04d08
        self.prefix = prefix
Packit Service a04d08
Packit Service a04d08
    def __enter__(self):
Packit Service a04d08
        """Create tempdir.
Packit Service a04d08
Packit Service a04d08
        @return_value: tempdir path
Packit Service a04d08
        """
Packit Service a04d08
        if not self.tmpdir:
Packit Service a04d08
            self.tmpdir = mkdtemp(prefix=self.prefix)
Packit Service a04d08
        LOG.debug('using tmpdir: %s', self.tmpdir)
Packit Service a04d08
        return self.tmpdir
Packit Service a04d08
Packit Service a04d08
    def __exit__(self, etype, value, trace):
Packit Service a04d08
        """Destroy tempdir if no errors occurred."""
Packit Service a04d08
        if etype or self.preserve:
Packit Service a04d08
            LOG.info('leaving data in %s', self.tmpdir)
Packit Service a04d08
        else:
Packit Service a04d08
            shutil.rmtree(self.tmpdir)
Packit Service a04d08
Packit Service a04d08
# vi: ts=4 expandtab