aboutsummaryrefslogtreecommitdiff
blob: 59525420ed5d963c0eee288d9e97f6539b22c26e (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
'''fileops.py

Performs file operations such as pack/unpack,
ensuring directories exist,... imports snakeoils osutils
functions for use throughout catalyst.
'''

import glob
import os
import shutil
from stat import ST_UID, ST_GID, ST_MODE

from snakeoil.osutils import ensure_dirs as snakeoil_ensure_dirs

from catalyst import log
from catalyst.support import CatalystError


def ensure_dirs(path, gid=-1, uid=-1, mode=0o755, minimal=True,
                failback=None, fatal=False):
    '''Wrapper to snakeoil.osutil's ensure_dirs()
    This additionally allows for failures to run
    cleanup or other code and/or raise fatal errors.

    :param path: directory to ensure exists on disk
    :param gid: a valid GID to set any created directories to
    :param uid: a valid UID to set any created directories to
    :param mode: permissions to set any created directories to
    :param minimal: boolean controlling whether or not the specified mode
            must be enforced, or is the minimal permissions necessary.  For example,
            if mode=0o755, minimal=True, and a directory exists with mode 0707,
            this will restore the missing group perms resulting in 757.
    :param failback: function to run in the event of a failed attemp
            to create the directory.
    :return: True if the directory could be created/ensured to have those
            permissions, False if not.
    '''
    succeeded = snakeoil_ensure_dirs(
        path, gid=gid, uid=uid, mode=mode, minimal=minimal)
    if not succeeded:
        if failback:
            failback()
        if fatal:
            raise CatalystError(
                "Failed to create directory: %s" % path, print_traceback=True)
    return succeeded


def clear_dir(target, mode=0o755, remove=False,
              clear_nondir=True):
    '''Universal directory clearing function

    @target: string, path to be cleared or removed
    @mode: integer, desired mode to set the directory to
    @remove: boolean, passed through to clear_dir()
    @return boolean
    '''
    log.debug('start: %s', target)
    if not target:
        log.debug('no target... returning')
        return False

    mystat = None
    if os.path.isdir(target) and not os.path.islink(target):
        log.notice('Emptying directory: %s', target)
        # stat the dir, delete the dir, recreate the dir and set
        # the proper perms and ownership
        try:
            log.debug('os.stat()')
            mystat = os.stat(target)
            log.debug('shutil.rmtree()')
            shutil.rmtree(target)
        except Exception:
            log.error('clear_dir failed', exc_info=True)
            return False
    elif os.path.exists(target):
        if clear_nondir:
            log.debug("Clearing (unlinking) non-directory: %s", target)
            os.unlink(target)
        else:
            log.info('clear_dir failed: %s: is not a directory', target)
            return False
    else:
        log.debug("Conditions not met to clear: %s", target)
        log.debug("                      isdir: %s", os.path.isdir(target))
        log.debug("                     islink: %s", os.path.islink(target))
        log.debug("                     exists: %s", os.path.exists(target))

    if not remove:
        log.debug('ensure_dirs()')
        ensure_dirs(target, mode=mode)
        if mystat:
            os.chown(target, mystat[ST_UID], mystat[ST_GID])
            os.chmod(target, mystat[ST_MODE])

    log.debug('DONE, returning True')
    return True


def clear_path(target_path):
    """Nuke |target_path| regardless of it being a dir, file or glob."""
    targets = glob.iglob(target_path, recursive=True)
    for path in targets:
        clear_dir(path, remove=True)


def move_path(src, dest):
    '''Move a source target to a new destination

    :param src: source path to move
    :param dest: destination path to move it to
    :returns: boolean
    '''
    log.debug('Start move_path(%s, %s)', src, dest)
    if os.path.isdir(src) and not os.path.islink(src):
        if os.path.exists(dest):
            log.warning('Removing existing target destination: %s', dest)
            if not clear_dir(dest, remove=True):
                return False
        log.debug('Moving source...')
        try:
            shutil.move(src, dest)
        except Exception:
            log.error('move_path failed', exc_info=True)
            return False
        return True
    return False