Statistics
| Branch: | Tag: | Revision:

root / lib / utils / io.py @ 570274e4

History | View | Annotate | Download (29 kB)

1 3865ca48 Michael Hanselmann
#
2 3865ca48 Michael Hanselmann
#
3 3865ca48 Michael Hanselmann
4 5ae4945a Iustin Pop
# Copyright (C) 2006, 2007, 2010, 2011, 2012 Google Inc.
5 3865ca48 Michael Hanselmann
#
6 3865ca48 Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
7 3865ca48 Michael Hanselmann
# it under the terms of the GNU General Public License as published by
8 3865ca48 Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
9 3865ca48 Michael Hanselmann
# (at your option) any later version.
10 3865ca48 Michael Hanselmann
#
11 3865ca48 Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
12 3865ca48 Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 3865ca48 Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 3865ca48 Michael Hanselmann
# General Public License for more details.
15 3865ca48 Michael Hanselmann
#
16 3865ca48 Michael Hanselmann
# You should have received a copy of the GNU General Public License
17 3865ca48 Michael Hanselmann
# along with this program; if not, write to the Free Software
18 3865ca48 Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 3865ca48 Michael Hanselmann
# 02110-1301, USA.
20 3865ca48 Michael Hanselmann
21 3865ca48 Michael Hanselmann
"""Utility functions for I/O.
22 3865ca48 Michael Hanselmann

23 3865ca48 Michael Hanselmann
"""
24 3865ca48 Michael Hanselmann
25 3865ca48 Michael Hanselmann
import os
26 3865ca48 Michael Hanselmann
import logging
27 3865ca48 Michael Hanselmann
import shutil
28 3865ca48 Michael Hanselmann
import tempfile
29 3865ca48 Michael Hanselmann
import errno
30 3865ca48 Michael Hanselmann
import time
31 b81b3c96 René Nussbaumer
import stat
32 3865ca48 Michael Hanselmann
33 3865ca48 Michael Hanselmann
from ganeti import errors
34 3865ca48 Michael Hanselmann
from ganeti import constants
35 111a7d04 Michael Hanselmann
from ganeti import pathutils
36 3865ca48 Michael Hanselmann
from ganeti.utils import filelock
37 3865ca48 Michael Hanselmann
38 3865ca48 Michael Hanselmann
39 90e234a6 Michael Hanselmann
#: Path generating random UUID
40 90e234a6 Michael Hanselmann
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
41 90e234a6 Michael Hanselmann
42 2dbc6857 Michael Hanselmann
#: Directory used by fsck(8) to store recovered data, usually at a file
43 2dbc6857 Michael Hanselmann
#: system's root directory
44 2dbc6857 Michael Hanselmann
_LOST_AND_FOUND = "lost+found"
45 2dbc6857 Michael Hanselmann
46 c47eddb8 Bernardo Dal Seno
# Possible values for keep_perms in WriteFile()
47 c47eddb8 Bernardo Dal Seno
KP_NEVER = 0
48 c47eddb8 Bernardo Dal Seno
KP_ALWAYS = 1
49 c47eddb8 Bernardo Dal Seno
KP_IF_EXISTS = 2
50 c47eddb8 Bernardo Dal Seno
51 c47eddb8 Bernardo Dal Seno
KEEP_PERMS_VALUES = [
52 c47eddb8 Bernardo Dal Seno
  KP_NEVER,
53 c47eddb8 Bernardo Dal Seno
  KP_ALWAYS,
54 c47eddb8 Bernardo Dal Seno
  KP_IF_EXISTS,
55 c47eddb8 Bernardo Dal Seno
  ]
56 c47eddb8 Bernardo Dal Seno
57 90e234a6 Michael Hanselmann
58 eb93b673 Guido Trotter
def ErrnoOrStr(err):
59 eb93b673 Guido Trotter
  """Format an EnvironmentError exception.
60 eb93b673 Guido Trotter

61 eb93b673 Guido Trotter
  If the L{err} argument has an errno attribute, it will be looked up
62 eb93b673 Guido Trotter
  and converted into a textual C{E...} description. Otherwise the
63 eb93b673 Guido Trotter
  string representation of the error will be returned.
64 eb93b673 Guido Trotter

65 eb93b673 Guido Trotter
  @type err: L{EnvironmentError}
66 eb93b673 Guido Trotter
  @param err: the exception to format
67 eb93b673 Guido Trotter

68 eb93b673 Guido Trotter
  """
69 eb93b673 Guido Trotter
  if hasattr(err, "errno"):
70 eb93b673 Guido Trotter
    detail = errno.errorcode[err.errno]
71 eb93b673 Guido Trotter
  else:
72 eb93b673 Guido Trotter
    detail = str(err)
73 eb93b673 Guido Trotter
  return detail
74 eb93b673 Guido Trotter
75 eb93b673 Guido Trotter
76 2635bb04 Michael Hanselmann
class FileStatHelper:
77 2635bb04 Michael Hanselmann
  """Helper to store file handle's C{fstat}.
78 2635bb04 Michael Hanselmann

79 2635bb04 Michael Hanselmann
  Useful in combination with L{ReadFile}'s C{preread} parameter.
80 2635bb04 Michael Hanselmann

81 2635bb04 Michael Hanselmann
  """
82 2635bb04 Michael Hanselmann
  def __init__(self):
83 2635bb04 Michael Hanselmann
    """Initializes this class.
84 2635bb04 Michael Hanselmann

85 2635bb04 Michael Hanselmann
    """
86 2635bb04 Michael Hanselmann
    self.st = None
87 2635bb04 Michael Hanselmann
88 2635bb04 Michael Hanselmann
  def __call__(self, fh):
89 2635bb04 Michael Hanselmann
    """Calls C{fstat} on file handle.
90 2635bb04 Michael Hanselmann

91 2635bb04 Michael Hanselmann
    """
92 2635bb04 Michael Hanselmann
    self.st = os.fstat(fh.fileno())
93 2635bb04 Michael Hanselmann
94 2635bb04 Michael Hanselmann
95 0e5084ee Michael Hanselmann
def ReadFile(file_name, size=-1, preread=None):
96 3865ca48 Michael Hanselmann
  """Reads a file.
97 3865ca48 Michael Hanselmann

98 3865ca48 Michael Hanselmann
  @type size: int
99 3865ca48 Michael Hanselmann
  @param size: Read at most size bytes (if negative, entire file)
100 0e5084ee Michael Hanselmann
  @type preread: callable receiving file handle as single parameter
101 0e5084ee Michael Hanselmann
  @param preread: Function called before file is read
102 3865ca48 Michael Hanselmann
  @rtype: str
103 3865ca48 Michael Hanselmann
  @return: the (possibly partial) content of the file
104 3865ca48 Michael Hanselmann

105 3865ca48 Michael Hanselmann
  """
106 3865ca48 Michael Hanselmann
  f = open(file_name, "r")
107 3865ca48 Michael Hanselmann
  try:
108 0e5084ee Michael Hanselmann
    if preread:
109 0e5084ee Michael Hanselmann
      preread(f)
110 0e5084ee Michael Hanselmann
111 3865ca48 Michael Hanselmann
    return f.read(size)
112 3865ca48 Michael Hanselmann
  finally:
113 3865ca48 Michael Hanselmann
    f.close()
114 3865ca48 Michael Hanselmann
115 3865ca48 Michael Hanselmann
116 3865ca48 Michael Hanselmann
def WriteFile(file_name, fn=None, data=None,
117 3865ca48 Michael Hanselmann
              mode=None, uid=-1, gid=-1,
118 3865ca48 Michael Hanselmann
              atime=None, mtime=None, close=True,
119 3865ca48 Michael Hanselmann
              dry_run=False, backup=False,
120 c47eddb8 Bernardo Dal Seno
              prewrite=None, postwrite=None, keep_perms=KP_NEVER):
121 3865ca48 Michael Hanselmann
  """(Over)write a file atomically.
122 3865ca48 Michael Hanselmann

123 3865ca48 Michael Hanselmann
  The file_name and either fn (a function taking one argument, the
124 3865ca48 Michael Hanselmann
  file descriptor, and which should write the data to it) or data (the
125 3865ca48 Michael Hanselmann
  contents of the file) must be passed. The other arguments are
126 3865ca48 Michael Hanselmann
  optional and allow setting the file mode, owner and group, and the
127 3865ca48 Michael Hanselmann
  mtime/atime of the file.
128 3865ca48 Michael Hanselmann

129 3865ca48 Michael Hanselmann
  If the function doesn't raise an exception, it has succeeded and the
130 3865ca48 Michael Hanselmann
  target file has the new contents. If the function has raised an
131 3865ca48 Michael Hanselmann
  exception, an existing target file should be unmodified and the
132 3865ca48 Michael Hanselmann
  temporary file should be removed.
133 3865ca48 Michael Hanselmann

134 3865ca48 Michael Hanselmann
  @type file_name: str
135 3865ca48 Michael Hanselmann
  @param file_name: the target filename
136 3865ca48 Michael Hanselmann
  @type fn: callable
137 3865ca48 Michael Hanselmann
  @param fn: content writing function, called with
138 3865ca48 Michael Hanselmann
      file descriptor as parameter
139 3865ca48 Michael Hanselmann
  @type data: str
140 3865ca48 Michael Hanselmann
  @param data: contents of the file
141 3865ca48 Michael Hanselmann
  @type mode: int
142 3865ca48 Michael Hanselmann
  @param mode: file mode
143 3865ca48 Michael Hanselmann
  @type uid: int
144 3865ca48 Michael Hanselmann
  @param uid: the owner of the file
145 3865ca48 Michael Hanselmann
  @type gid: int
146 3865ca48 Michael Hanselmann
  @param gid: the group of the file
147 3865ca48 Michael Hanselmann
  @type atime: int
148 3865ca48 Michael Hanselmann
  @param atime: a custom access time to be set on the file
149 3865ca48 Michael Hanselmann
  @type mtime: int
150 3865ca48 Michael Hanselmann
  @param mtime: a custom modification time to be set on the file
151 3865ca48 Michael Hanselmann
  @type close: boolean
152 3865ca48 Michael Hanselmann
  @param close: whether to close file after writing it
153 3865ca48 Michael Hanselmann
  @type prewrite: callable
154 3865ca48 Michael Hanselmann
  @param prewrite: function to be called before writing content
155 3865ca48 Michael Hanselmann
  @type postwrite: callable
156 3865ca48 Michael Hanselmann
  @param postwrite: function to be called after writing content
157 c47eddb8 Bernardo Dal Seno
  @type keep_perms: members of L{KEEP_PERMS_VALUES}
158 c47eddb8 Bernardo Dal Seno
  @param keep_perms: if L{KP_NEVER} (default), owner, group, and mode are
159 c47eddb8 Bernardo Dal Seno
      taken from the other parameters; if L{KP_ALWAYS}, owner, group, and
160 c47eddb8 Bernardo Dal Seno
      mode are copied from the existing file; if L{KP_IF_EXISTS}, owner,
161 c47eddb8 Bernardo Dal Seno
      group, and mode are taken from the file, and if the file doesn't
162 c47eddb8 Bernardo Dal Seno
      exist, they are taken from the other parameters. It is an error to
163 c47eddb8 Bernardo Dal Seno
      pass L{KP_ALWAYS} when the file doesn't exist or when C{uid}, C{gid},
164 c47eddb8 Bernardo Dal Seno
      or C{mode} are set to non-default values.
165 3865ca48 Michael Hanselmann

166 3865ca48 Michael Hanselmann
  @rtype: None or int
167 3865ca48 Michael Hanselmann
  @return: None if the 'close' parameter evaluates to True,
168 3865ca48 Michael Hanselmann
      otherwise the file descriptor
169 3865ca48 Michael Hanselmann

170 3865ca48 Michael Hanselmann
  @raise errors.ProgrammerError: if any of the arguments are not valid
171 3865ca48 Michael Hanselmann

172 3865ca48 Michael Hanselmann
  """
173 3865ca48 Michael Hanselmann
  if not os.path.isabs(file_name):
174 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("Path passed to WriteFile is not"
175 3865ca48 Michael Hanselmann
                                 " absolute: '%s'" % file_name)
176 3865ca48 Michael Hanselmann
177 3865ca48 Michael Hanselmann
  if [fn, data].count(None) != 1:
178 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("fn or data required")
179 3865ca48 Michael Hanselmann
180 3865ca48 Michael Hanselmann
  if [atime, mtime].count(None) == 1:
181 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("Both atime and mtime must be either"
182 3865ca48 Michael Hanselmann
                                 " set or None")
183 3865ca48 Michael Hanselmann
184 c47eddb8 Bernardo Dal Seno
  if not keep_perms in KEEP_PERMS_VALUES:
185 c47eddb8 Bernardo Dal Seno
    raise errors.ProgrammerError("Invalid value for keep_perms: %s" %
186 c47eddb8 Bernardo Dal Seno
                                 keep_perms)
187 c47eddb8 Bernardo Dal Seno
  if keep_perms == KP_ALWAYS and (uid != -1 or gid != -1 or mode is not None):
188 c47eddb8 Bernardo Dal Seno
    raise errors.ProgrammerError("When keep_perms==KP_ALWAYS, 'uid', 'gid',"
189 c47eddb8 Bernardo Dal Seno
                                 " and 'mode' cannot be set")
190 c47eddb8 Bernardo Dal Seno
191 3865ca48 Michael Hanselmann
  if backup and not dry_run and os.path.isfile(file_name):
192 3865ca48 Michael Hanselmann
    CreateBackup(file_name)
193 3865ca48 Michael Hanselmann
194 c47eddb8 Bernardo Dal Seno
  if keep_perms == KP_ALWAYS or keep_perms == KP_IF_EXISTS:
195 c47eddb8 Bernardo Dal Seno
    # os.stat() raises an exception if the file doesn't exist
196 c47eddb8 Bernardo Dal Seno
    try:
197 c47eddb8 Bernardo Dal Seno
      file_stat = os.stat(file_name)
198 c47eddb8 Bernardo Dal Seno
      mode = stat.S_IMODE(file_stat.st_mode)
199 c47eddb8 Bernardo Dal Seno
      uid = file_stat.st_uid
200 c47eddb8 Bernardo Dal Seno
      gid = file_stat.st_gid
201 c47eddb8 Bernardo Dal Seno
    except OSError:
202 c47eddb8 Bernardo Dal Seno
      if keep_perms == KP_ALWAYS:
203 c47eddb8 Bernardo Dal Seno
        raise
204 c47eddb8 Bernardo Dal Seno
      # else: if keeep_perms == KP_IF_EXISTS it's ok if the file doesn't exist
205 c47eddb8 Bernardo Dal Seno
206 a9d68e40 Michael Hanselmann
  # Whether temporary file needs to be removed (e.g. if any error occurs)
207 3865ca48 Michael Hanselmann
  do_remove = True
208 a9d68e40 Michael Hanselmann
209 a9d68e40 Michael Hanselmann
  # Function result
210 a9d68e40 Michael Hanselmann
  result = None
211 a9d68e40 Michael Hanselmann
212 a9d68e40 Michael Hanselmann
  (dir_name, base_name) = os.path.split(file_name)
213 a9d68e40 Michael Hanselmann
  (fd, new_name) = tempfile.mkstemp(suffix=".new", prefix=base_name,
214 a9d68e40 Michael Hanselmann
                                    dir=dir_name)
215 3865ca48 Michael Hanselmann
  try:
216 a9d68e40 Michael Hanselmann
    try:
217 a9d68e40 Michael Hanselmann
      if uid != -1 or gid != -1:
218 a9d68e40 Michael Hanselmann
        os.chown(new_name, uid, gid)
219 a9d68e40 Michael Hanselmann
      if mode:
220 a9d68e40 Michael Hanselmann
        os.chmod(new_name, mode)
221 a9d68e40 Michael Hanselmann
      if callable(prewrite):
222 a9d68e40 Michael Hanselmann
        prewrite(fd)
223 a9d68e40 Michael Hanselmann
      if data is not None:
224 1d39e245 Iustin Pop
        if isinstance(data, unicode):
225 1d39e245 Iustin Pop
          data = data.encode()
226 1d39e245 Iustin Pop
        assert isinstance(data, str)
227 437c3e77 Iustin Pop
        to_write = len(data)
228 437c3e77 Iustin Pop
        offset = 0
229 437c3e77 Iustin Pop
        while offset < to_write:
230 437c3e77 Iustin Pop
          written = os.write(fd, buffer(data, offset))
231 437c3e77 Iustin Pop
          assert written >= 0
232 1d39e245 Iustin Pop
          assert written <= to_write - offset
233 437c3e77 Iustin Pop
          offset += written
234 437c3e77 Iustin Pop
        assert offset == to_write
235 a9d68e40 Michael Hanselmann
      else:
236 a9d68e40 Michael Hanselmann
        fn(fd)
237 a9d68e40 Michael Hanselmann
      if callable(postwrite):
238 a9d68e40 Michael Hanselmann
        postwrite(fd)
239 a9d68e40 Michael Hanselmann
      os.fsync(fd)
240 a9d68e40 Michael Hanselmann
      if atime is not None and mtime is not None:
241 a9d68e40 Michael Hanselmann
        os.utime(new_name, (atime, mtime))
242 a9d68e40 Michael Hanselmann
    finally:
243 a9d68e40 Michael Hanselmann
      # Close file unless the file descriptor should be returned
244 a9d68e40 Michael Hanselmann
      if close:
245 a9d68e40 Michael Hanselmann
        os.close(fd)
246 a9d68e40 Michael Hanselmann
      else:
247 a9d68e40 Michael Hanselmann
        result = fd
248 a9d68e40 Michael Hanselmann
249 a9d68e40 Michael Hanselmann
    # Rename file to destination name
250 3865ca48 Michael Hanselmann
    if not dry_run:
251 3865ca48 Michael Hanselmann
      os.rename(new_name, file_name)
252 a9d68e40 Michael Hanselmann
      # Successful, no need to remove anymore
253 3865ca48 Michael Hanselmann
      do_remove = False
254 3865ca48 Michael Hanselmann
  finally:
255 3865ca48 Michael Hanselmann
    if do_remove:
256 3865ca48 Michael Hanselmann
      RemoveFile(new_name)
257 3865ca48 Michael Hanselmann
258 3865ca48 Michael Hanselmann
  return result
259 3865ca48 Michael Hanselmann
260 3865ca48 Michael Hanselmann
261 3865ca48 Michael Hanselmann
def GetFileID(path=None, fd=None):
262 3865ca48 Michael Hanselmann
  """Returns the file 'id', i.e. the dev/inode and mtime information.
263 3865ca48 Michael Hanselmann

264 3865ca48 Michael Hanselmann
  Either the path to the file or the fd must be given.
265 3865ca48 Michael Hanselmann

266 3865ca48 Michael Hanselmann
  @param path: the file path
267 3865ca48 Michael Hanselmann
  @param fd: a file descriptor
268 3865ca48 Michael Hanselmann
  @return: a tuple of (device number, inode number, mtime)
269 3865ca48 Michael Hanselmann

270 3865ca48 Michael Hanselmann
  """
271 3865ca48 Michael Hanselmann
  if [path, fd].count(None) != 1:
272 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("One and only one of fd/path must be given")
273 3865ca48 Michael Hanselmann
274 3865ca48 Michael Hanselmann
  if fd is None:
275 3865ca48 Michael Hanselmann
    st = os.stat(path)
276 3865ca48 Michael Hanselmann
  else:
277 3865ca48 Michael Hanselmann
    st = os.fstat(fd)
278 3865ca48 Michael Hanselmann
279 3865ca48 Michael Hanselmann
  return (st.st_dev, st.st_ino, st.st_mtime)
280 3865ca48 Michael Hanselmann
281 3865ca48 Michael Hanselmann
282 3865ca48 Michael Hanselmann
def VerifyFileID(fi_disk, fi_ours):
283 3865ca48 Michael Hanselmann
  """Verifies that two file IDs are matching.
284 3865ca48 Michael Hanselmann

285 3865ca48 Michael Hanselmann
  Differences in the inode/device are not accepted, but and older
286 3865ca48 Michael Hanselmann
  timestamp for fi_disk is accepted.
287 3865ca48 Michael Hanselmann

288 3865ca48 Michael Hanselmann
  @param fi_disk: tuple (dev, inode, mtime) representing the actual
289 3865ca48 Michael Hanselmann
      file data
290 3865ca48 Michael Hanselmann
  @param fi_ours: tuple (dev, inode, mtime) representing the last
291 3865ca48 Michael Hanselmann
      written file data
292 3865ca48 Michael Hanselmann
  @rtype: boolean
293 3865ca48 Michael Hanselmann

294 3865ca48 Michael Hanselmann
  """
295 3865ca48 Michael Hanselmann
  (d1, i1, m1) = fi_disk
296 3865ca48 Michael Hanselmann
  (d2, i2, m2) = fi_ours
297 3865ca48 Michael Hanselmann
298 3865ca48 Michael Hanselmann
  return (d1, i1) == (d2, i2) and m1 <= m2
299 3865ca48 Michael Hanselmann
300 3865ca48 Michael Hanselmann
301 3865ca48 Michael Hanselmann
def SafeWriteFile(file_name, file_id, **kwargs):
302 3865ca48 Michael Hanselmann
  """Wraper over L{WriteFile} that locks the target file.
303 3865ca48 Michael Hanselmann

304 3865ca48 Michael Hanselmann
  By keeping the target file locked during WriteFile, we ensure that
305 3865ca48 Michael Hanselmann
  cooperating writers will safely serialise access to the file.
306 3865ca48 Michael Hanselmann

307 3865ca48 Michael Hanselmann
  @type file_name: str
308 3865ca48 Michael Hanselmann
  @param file_name: the target filename
309 3865ca48 Michael Hanselmann
  @type file_id: tuple
310 3865ca48 Michael Hanselmann
  @param file_id: a result from L{GetFileID}
311 3865ca48 Michael Hanselmann

312 3865ca48 Michael Hanselmann
  """
313 3865ca48 Michael Hanselmann
  fd = os.open(file_name, os.O_RDONLY | os.O_CREAT)
314 3865ca48 Michael Hanselmann
  try:
315 3865ca48 Michael Hanselmann
    filelock.LockFile(fd)
316 3865ca48 Michael Hanselmann
    if file_id is not None:
317 3865ca48 Michael Hanselmann
      disk_id = GetFileID(fd=fd)
318 3865ca48 Michael Hanselmann
      if not VerifyFileID(disk_id, file_id):
319 3865ca48 Michael Hanselmann
        raise errors.LockError("Cannot overwrite file %s, it has been modified"
320 3865ca48 Michael Hanselmann
                               " since last written" % file_name)
321 3865ca48 Michael Hanselmann
    return WriteFile(file_name, **kwargs)
322 3865ca48 Michael Hanselmann
  finally:
323 3865ca48 Michael Hanselmann
    os.close(fd)
324 3865ca48 Michael Hanselmann
325 3865ca48 Michael Hanselmann
326 3865ca48 Michael Hanselmann
def ReadOneLineFile(file_name, strict=False):
327 3865ca48 Michael Hanselmann
  """Return the first non-empty line from a file.
328 3865ca48 Michael Hanselmann

329 3865ca48 Michael Hanselmann
  @type strict: boolean
330 3865ca48 Michael Hanselmann
  @param strict: if True, abort if the file has more than one
331 3865ca48 Michael Hanselmann
      non-empty line
332 3865ca48 Michael Hanselmann

333 3865ca48 Michael Hanselmann
  """
334 3865ca48 Michael Hanselmann
  file_lines = ReadFile(file_name).splitlines()
335 3865ca48 Michael Hanselmann
  full_lines = filter(bool, file_lines)
336 3865ca48 Michael Hanselmann
  if not file_lines or not full_lines:
337 3865ca48 Michael Hanselmann
    raise errors.GenericError("No data in one-liner file %s" % file_name)
338 3865ca48 Michael Hanselmann
  elif strict and len(full_lines) > 1:
339 3865ca48 Michael Hanselmann
    raise errors.GenericError("Too many lines in one-liner file %s" %
340 3865ca48 Michael Hanselmann
                              file_name)
341 3865ca48 Michael Hanselmann
  return full_lines[0]
342 3865ca48 Michael Hanselmann
343 3865ca48 Michael Hanselmann
344 3865ca48 Michael Hanselmann
def RemoveFile(filename):
345 3865ca48 Michael Hanselmann
  """Remove a file ignoring some errors.
346 3865ca48 Michael Hanselmann

347 3865ca48 Michael Hanselmann
  Remove a file, ignoring non-existing ones or directories. Other
348 3865ca48 Michael Hanselmann
  errors are passed.
349 3865ca48 Michael Hanselmann

350 3865ca48 Michael Hanselmann
  @type filename: str
351 3865ca48 Michael Hanselmann
  @param filename: the file to be removed
352 3865ca48 Michael Hanselmann

353 3865ca48 Michael Hanselmann
  """
354 3865ca48 Michael Hanselmann
  try:
355 3865ca48 Michael Hanselmann
    os.unlink(filename)
356 3865ca48 Michael Hanselmann
  except OSError, err:
357 3865ca48 Michael Hanselmann
    if err.errno not in (errno.ENOENT, errno.EISDIR):
358 3865ca48 Michael Hanselmann
      raise
359 3865ca48 Michael Hanselmann
360 3865ca48 Michael Hanselmann
361 3865ca48 Michael Hanselmann
def RemoveDir(dirname):
362 3865ca48 Michael Hanselmann
  """Remove an empty directory.
363 3865ca48 Michael Hanselmann

364 3865ca48 Michael Hanselmann
  Remove a directory, ignoring non-existing ones.
365 3865ca48 Michael Hanselmann
  Other errors are passed. This includes the case,
366 3865ca48 Michael Hanselmann
  where the directory is not empty, so it can't be removed.
367 3865ca48 Michael Hanselmann

368 3865ca48 Michael Hanselmann
  @type dirname: str
369 3865ca48 Michael Hanselmann
  @param dirname: the empty directory to be removed
370 3865ca48 Michael Hanselmann

371 3865ca48 Michael Hanselmann
  """
372 3865ca48 Michael Hanselmann
  try:
373 3865ca48 Michael Hanselmann
    os.rmdir(dirname)
374 3865ca48 Michael Hanselmann
  except OSError, err:
375 3865ca48 Michael Hanselmann
    if err.errno != errno.ENOENT:
376 3865ca48 Michael Hanselmann
      raise
377 3865ca48 Michael Hanselmann
378 3865ca48 Michael Hanselmann
379 8e5a705d René Nussbaumer
def RenameFile(old, new, mkdir=False, mkdir_mode=0750, dir_uid=None,
380 8e5a705d René Nussbaumer
               dir_gid=None):
381 3865ca48 Michael Hanselmann
  """Renames a file.
382 3865ca48 Michael Hanselmann

383 9c2b3a70 René Nussbaumer
  This just creates the very least directory if it does not exist and C{mkdir}
384 9c2b3a70 René Nussbaumer
  is set to true.
385 9c2b3a70 René Nussbaumer

386 3865ca48 Michael Hanselmann
  @type old: string
387 3865ca48 Michael Hanselmann
  @param old: Original path
388 3865ca48 Michael Hanselmann
  @type new: string
389 3865ca48 Michael Hanselmann
  @param new: New path
390 3865ca48 Michael Hanselmann
  @type mkdir: bool
391 3865ca48 Michael Hanselmann
  @param mkdir: Whether to create target directory if it doesn't exist
392 3865ca48 Michael Hanselmann
  @type mkdir_mode: int
393 3865ca48 Michael Hanselmann
  @param mkdir_mode: Mode for newly created directories
394 8e5a705d René Nussbaumer
  @type dir_uid: int
395 8e5a705d René Nussbaumer
  @param dir_uid: The uid for the (if fresh created) dir
396 8e5a705d René Nussbaumer
  @type dir_gid: int
397 8e5a705d René Nussbaumer
  @param dir_gid: The gid for the (if fresh created) dir
398 3865ca48 Michael Hanselmann

399 3865ca48 Michael Hanselmann
  """
400 3865ca48 Michael Hanselmann
  try:
401 3865ca48 Michael Hanselmann
    return os.rename(old, new)
402 3865ca48 Michael Hanselmann
  except OSError, err:
403 3865ca48 Michael Hanselmann
    # In at least one use case of this function, the job queue, directory
404 3865ca48 Michael Hanselmann
    # creation is very rare. Checking for the directory before renaming is not
405 3865ca48 Michael Hanselmann
    # as efficient.
406 3865ca48 Michael Hanselmann
    if mkdir and err.errno == errno.ENOENT:
407 3865ca48 Michael Hanselmann
      # Create directory and try again
408 8e5a705d René Nussbaumer
      dir_path = os.path.dirname(new)
409 9c2b3a70 René Nussbaumer
      MakeDirWithPerm(dir_path, mkdir_mode, dir_uid, dir_gid)
410 3865ca48 Michael Hanselmann
411 3865ca48 Michael Hanselmann
      return os.rename(old, new)
412 3865ca48 Michael Hanselmann
413 3865ca48 Michael Hanselmann
    raise
414 3865ca48 Michael Hanselmann
415 3865ca48 Michael Hanselmann
416 9c2b3a70 René Nussbaumer
def EnforcePermission(path, mode, uid=None, gid=None, must_exist=True,
417 b81b3c96 René Nussbaumer
                      _chmod_fn=os.chmod, _chown_fn=os.chown, _stat_fn=os.stat):
418 b81b3c96 René Nussbaumer
  """Enforces that given path has given permissions.
419 b81b3c96 René Nussbaumer

420 b81b3c96 René Nussbaumer
  @param path: The path to the file
421 b81b3c96 René Nussbaumer
  @param mode: The mode of the file
422 b81b3c96 René Nussbaumer
  @param uid: The uid of the owner of this file
423 b81b3c96 René Nussbaumer
  @param gid: The gid of the owner of this file
424 b81b3c96 René Nussbaumer
  @param must_exist: Specifies if non-existance of path will be an error
425 b81b3c96 René Nussbaumer
  @param _chmod_fn: chmod function to use (unittest only)
426 b81b3c96 René Nussbaumer
  @param _chown_fn: chown function to use (unittest only)
427 b81b3c96 René Nussbaumer

428 b81b3c96 René Nussbaumer
  """
429 b81b3c96 René Nussbaumer
  logging.debug("Checking %s", path)
430 9c2b3a70 René Nussbaumer
431 9c2b3a70 René Nussbaumer
  # chown takes -1 if you want to keep one part of the ownership, however
432 9c2b3a70 René Nussbaumer
  # None is Python standard for that. So we remap them here.
433 9c2b3a70 René Nussbaumer
  if uid is None:
434 9c2b3a70 René Nussbaumer
    uid = -1
435 9c2b3a70 René Nussbaumer
  if gid is None:
436 9c2b3a70 René Nussbaumer
    gid = -1
437 9c2b3a70 René Nussbaumer
438 b81b3c96 René Nussbaumer
  try:
439 b81b3c96 René Nussbaumer
    st = _stat_fn(path)
440 b81b3c96 René Nussbaumer
441 b81b3c96 René Nussbaumer
    fmode = stat.S_IMODE(st[stat.ST_MODE])
442 b81b3c96 René Nussbaumer
    if fmode != mode:
443 b81b3c96 René Nussbaumer
      logging.debug("Changing mode of %s from %#o to %#o", path, fmode, mode)
444 b81b3c96 René Nussbaumer
      _chmod_fn(path, mode)
445 b81b3c96 René Nussbaumer
446 b81b3c96 René Nussbaumer
    if max(uid, gid) > -1:
447 b81b3c96 René Nussbaumer
      fuid = st[stat.ST_UID]
448 b81b3c96 René Nussbaumer
      fgid = st[stat.ST_GID]
449 b81b3c96 René Nussbaumer
      if fuid != uid or fgid != gid:
450 b81b3c96 René Nussbaumer
        logging.debug("Changing owner of %s from UID %s/GID %s to"
451 b81b3c96 René Nussbaumer
                      " UID %s/GID %s", path, fuid, fgid, uid, gid)
452 b81b3c96 René Nussbaumer
        _chown_fn(path, uid, gid)
453 b81b3c96 René Nussbaumer
  except EnvironmentError, err:
454 b81b3c96 René Nussbaumer
    if err.errno == errno.ENOENT:
455 b81b3c96 René Nussbaumer
      if must_exist:
456 b81b3c96 René Nussbaumer
        raise errors.GenericError("Path %s should exist, but does not" % path)
457 b81b3c96 René Nussbaumer
    else:
458 b81b3c96 René Nussbaumer
      raise errors.GenericError("Error while changing permissions on %s: %s" %
459 b81b3c96 René Nussbaumer
                                (path, err))
460 b81b3c96 René Nussbaumer
461 b81b3c96 René Nussbaumer
462 b81b3c96 René Nussbaumer
def MakeDirWithPerm(path, mode, uid, gid, _lstat_fn=os.lstat,
463 b81b3c96 René Nussbaumer
                    _mkdir_fn=os.mkdir, _perm_fn=EnforcePermission):
464 b81b3c96 René Nussbaumer
  """Enforces that given path is a dir and has given mode, uid and gid set.
465 b81b3c96 René Nussbaumer

466 b81b3c96 René Nussbaumer
  @param path: The path to the file
467 b81b3c96 René Nussbaumer
  @param mode: The mode of the file
468 b81b3c96 René Nussbaumer
  @param uid: The uid of the owner of this file
469 b81b3c96 René Nussbaumer
  @param gid: The gid of the owner of this file
470 b81b3c96 René Nussbaumer
  @param _lstat_fn: Stat function to use (unittest only)
471 b81b3c96 René Nussbaumer
  @param _mkdir_fn: mkdir function to use (unittest only)
472 b81b3c96 René Nussbaumer
  @param _perm_fn: permission setter function to use (unittest only)
473 b81b3c96 René Nussbaumer

474 b81b3c96 René Nussbaumer
  """
475 b81b3c96 René Nussbaumer
  logging.debug("Checking directory %s", path)
476 b81b3c96 René Nussbaumer
  try:
477 b81b3c96 René Nussbaumer
    # We don't want to follow symlinks
478 b81b3c96 René Nussbaumer
    st = _lstat_fn(path)
479 b81b3c96 René Nussbaumer
  except EnvironmentError, err:
480 b81b3c96 René Nussbaumer
    if err.errno != errno.ENOENT:
481 b81b3c96 René Nussbaumer
      raise errors.GenericError("stat(2) on %s failed: %s" % (path, err))
482 b81b3c96 René Nussbaumer
    _mkdir_fn(path)
483 b81b3c96 René Nussbaumer
  else:
484 b81b3c96 René Nussbaumer
    if not stat.S_ISDIR(st[stat.ST_MODE]):
485 b81b3c96 René Nussbaumer
      raise errors.GenericError(("Path %s is expected to be a directory, but "
486 b81b3c96 René Nussbaumer
                                 "isn't") % path)
487 b81b3c96 René Nussbaumer
488 b81b3c96 René Nussbaumer
  _perm_fn(path, mode, uid=uid, gid=gid)
489 b81b3c96 René Nussbaumer
490 b81b3c96 René Nussbaumer
491 3865ca48 Michael Hanselmann
def Makedirs(path, mode=0750):
492 3865ca48 Michael Hanselmann
  """Super-mkdir; create a leaf directory and all intermediate ones.
493 3865ca48 Michael Hanselmann

494 3865ca48 Michael Hanselmann
  This is a wrapper around C{os.makedirs} adding error handling not implemented
495 3865ca48 Michael Hanselmann
  before Python 2.5.
496 3865ca48 Michael Hanselmann

497 3865ca48 Michael Hanselmann
  """
498 3865ca48 Michael Hanselmann
  try:
499 3865ca48 Michael Hanselmann
    os.makedirs(path, mode)
500 3865ca48 Michael Hanselmann
  except OSError, err:
501 3865ca48 Michael Hanselmann
    # Ignore EEXIST. This is only handled in os.makedirs as included in
502 3865ca48 Michael Hanselmann
    # Python 2.5 and above.
503 3865ca48 Michael Hanselmann
    if err.errno != errno.EEXIST or not os.path.exists(path):
504 3865ca48 Michael Hanselmann
      raise
505 3865ca48 Michael Hanselmann
506 3865ca48 Michael Hanselmann
507 3865ca48 Michael Hanselmann
def TimestampForFilename():
508 3865ca48 Michael Hanselmann
  """Returns the current time formatted for filenames.
509 3865ca48 Michael Hanselmann

510 3865ca48 Michael Hanselmann
  The format doesn't contain colons as some shells and applications treat them
511 3865ca48 Michael Hanselmann
  as separators. Uses the local timezone.
512 3865ca48 Michael Hanselmann

513 3865ca48 Michael Hanselmann
  """
514 3865ca48 Michael Hanselmann
  return time.strftime("%Y-%m-%d_%H_%M_%S")
515 3865ca48 Michael Hanselmann
516 3865ca48 Michael Hanselmann
517 3865ca48 Michael Hanselmann
def CreateBackup(file_name):
518 3865ca48 Michael Hanselmann
  """Creates a backup of a file.
519 3865ca48 Michael Hanselmann

520 3865ca48 Michael Hanselmann
  @type file_name: str
521 3865ca48 Michael Hanselmann
  @param file_name: file to be backed up
522 3865ca48 Michael Hanselmann
  @rtype: str
523 3865ca48 Michael Hanselmann
  @return: the path to the newly created backup
524 3865ca48 Michael Hanselmann
  @raise errors.ProgrammerError: for invalid file names
525 3865ca48 Michael Hanselmann

526 3865ca48 Michael Hanselmann
  """
527 3865ca48 Michael Hanselmann
  if not os.path.isfile(file_name):
528 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
529 5ae4945a Iustin Pop
                                 file_name)
530 3865ca48 Michael Hanselmann
531 3865ca48 Michael Hanselmann
  prefix = ("%s.backup-%s." %
532 3865ca48 Michael Hanselmann
            (os.path.basename(file_name), TimestampForFilename()))
533 3865ca48 Michael Hanselmann
  dir_name = os.path.dirname(file_name)
534 3865ca48 Michael Hanselmann
535 d0c8c01d Iustin Pop
  fsrc = open(file_name, "rb")
536 3865ca48 Michael Hanselmann
  try:
537 3865ca48 Michael Hanselmann
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
538 d0c8c01d Iustin Pop
    fdst = os.fdopen(fd, "wb")
539 3865ca48 Michael Hanselmann
    try:
540 3865ca48 Michael Hanselmann
      logging.debug("Backing up %s at %s", file_name, backup_name)
541 3865ca48 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
542 3865ca48 Michael Hanselmann
    finally:
543 3865ca48 Michael Hanselmann
      fdst.close()
544 3865ca48 Michael Hanselmann
  finally:
545 3865ca48 Michael Hanselmann
    fsrc.close()
546 3865ca48 Michael Hanselmann
547 3865ca48 Michael Hanselmann
  return backup_name
548 3865ca48 Michael Hanselmann
549 3865ca48 Michael Hanselmann
550 2dbc6857 Michael Hanselmann
def ListVisibleFiles(path, _is_mountpoint=os.path.ismount):
551 3865ca48 Michael Hanselmann
  """Returns a list of visible files in a directory.
552 3865ca48 Michael Hanselmann

553 3865ca48 Michael Hanselmann
  @type path: str
554 3865ca48 Michael Hanselmann
  @param path: the directory to enumerate
555 3865ca48 Michael Hanselmann
  @rtype: list
556 3865ca48 Michael Hanselmann
  @return: the list of all files not starting with a dot
557 3865ca48 Michael Hanselmann
  @raise ProgrammerError: if L{path} is not an absolue and normalized path
558 3865ca48 Michael Hanselmann

559 3865ca48 Michael Hanselmann
  """
560 3865ca48 Michael Hanselmann
  if not IsNormAbsPath(path):
561 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("Path passed to ListVisibleFiles is not"
562 3865ca48 Michael Hanselmann
                                 " absolute/normalized: '%s'" % path)
563 2dbc6857 Michael Hanselmann
564 2dbc6857 Michael Hanselmann
  mountpoint = _is_mountpoint(path)
565 2dbc6857 Michael Hanselmann
566 2dbc6857 Michael Hanselmann
  def fn(name):
567 2dbc6857 Michael Hanselmann
    """File name filter.
568 2dbc6857 Michael Hanselmann

569 2dbc6857 Michael Hanselmann
    Ignores files starting with a dot (".") as by Unix convention they're
570 2dbc6857 Michael Hanselmann
    considered hidden. The "lost+found" directory found at the root of some
571 2dbc6857 Michael Hanselmann
    filesystems is also hidden.
572 2dbc6857 Michael Hanselmann

573 2dbc6857 Michael Hanselmann
    """
574 2dbc6857 Michael Hanselmann
    return not (name.startswith(".") or
575 2dbc6857 Michael Hanselmann
                (mountpoint and name == _LOST_AND_FOUND and
576 2dbc6857 Michael Hanselmann
                 os.path.isdir(os.path.join(path, name))))
577 2dbc6857 Michael Hanselmann
578 2dbc6857 Michael Hanselmann
  return filter(fn, os.listdir(path))
579 3865ca48 Michael Hanselmann
580 3865ca48 Michael Hanselmann
581 3865ca48 Michael Hanselmann
def EnsureDirs(dirs):
582 3865ca48 Michael Hanselmann
  """Make required directories, if they don't exist.
583 3865ca48 Michael Hanselmann

584 3865ca48 Michael Hanselmann
  @param dirs: list of tuples (dir_name, dir_mode)
585 3865ca48 Michael Hanselmann
  @type dirs: list of (string, integer)
586 3865ca48 Michael Hanselmann

587 3865ca48 Michael Hanselmann
  """
588 3865ca48 Michael Hanselmann
  for dir_name, dir_mode in dirs:
589 3865ca48 Michael Hanselmann
    try:
590 3865ca48 Michael Hanselmann
      os.mkdir(dir_name, dir_mode)
591 3865ca48 Michael Hanselmann
    except EnvironmentError, err:
592 3865ca48 Michael Hanselmann
      if err.errno != errno.EEXIST:
593 3865ca48 Michael Hanselmann
        raise errors.GenericError("Cannot create needed directory"
594 3865ca48 Michael Hanselmann
                                  " '%s': %s" % (dir_name, err))
595 3865ca48 Michael Hanselmann
    try:
596 3865ca48 Michael Hanselmann
      os.chmod(dir_name, dir_mode)
597 3865ca48 Michael Hanselmann
    except EnvironmentError, err:
598 3865ca48 Michael Hanselmann
      raise errors.GenericError("Cannot change directory permissions on"
599 3865ca48 Michael Hanselmann
                                " '%s': %s" % (dir_name, err))
600 3865ca48 Michael Hanselmann
    if not os.path.isdir(dir_name):
601 3865ca48 Michael Hanselmann
      raise errors.GenericError("%s is not a directory" % dir_name)
602 3865ca48 Michael Hanselmann
603 3865ca48 Michael Hanselmann
604 3865ca48 Michael Hanselmann
def FindFile(name, search_path, test=os.path.exists):
605 3865ca48 Michael Hanselmann
  """Look for a filesystem object in a given path.
606 3865ca48 Michael Hanselmann

607 3865ca48 Michael Hanselmann
  This is an abstract method to search for filesystem object (files,
608 3865ca48 Michael Hanselmann
  dirs) under a given search path.
609 3865ca48 Michael Hanselmann

610 3865ca48 Michael Hanselmann
  @type name: str
611 3865ca48 Michael Hanselmann
  @param name: the name to look for
612 3865ca48 Michael Hanselmann
  @type search_path: str
613 3865ca48 Michael Hanselmann
  @param search_path: location to start at
614 3865ca48 Michael Hanselmann
  @type test: callable
615 3865ca48 Michael Hanselmann
  @param test: a function taking one argument that should return True
616 3865ca48 Michael Hanselmann
      if the a given object is valid; the default value is
617 3865ca48 Michael Hanselmann
      os.path.exists, causing only existing files to be returned
618 3865ca48 Michael Hanselmann
  @rtype: str or None
619 3865ca48 Michael Hanselmann
  @return: full path to the object if found, None otherwise
620 3865ca48 Michael Hanselmann

621 3865ca48 Michael Hanselmann
  """
622 3865ca48 Michael Hanselmann
  # validate the filename mask
623 3865ca48 Michael Hanselmann
  if constants.EXT_PLUGIN_MASK.match(name) is None:
624 3865ca48 Michael Hanselmann
    logging.critical("Invalid value passed for external script name: '%s'",
625 3865ca48 Michael Hanselmann
                     name)
626 3865ca48 Michael Hanselmann
    return None
627 3865ca48 Michael Hanselmann
628 3865ca48 Michael Hanselmann
  for dir_name in search_path:
629 3865ca48 Michael Hanselmann
    # FIXME: investigate switch to PathJoin
630 3865ca48 Michael Hanselmann
    item_name = os.path.sep.join([dir_name, name])
631 3865ca48 Michael Hanselmann
    # check the user test and that we're indeed resolving to the given
632 3865ca48 Michael Hanselmann
    # basename
633 3865ca48 Michael Hanselmann
    if test(item_name) and os.path.basename(item_name) == name:
634 3865ca48 Michael Hanselmann
      return item_name
635 3865ca48 Michael Hanselmann
  return None
636 3865ca48 Michael Hanselmann
637 3865ca48 Michael Hanselmann
638 3865ca48 Michael Hanselmann
def IsNormAbsPath(path):
639 3865ca48 Michael Hanselmann
  """Check whether a path is absolute and also normalized
640 3865ca48 Michael Hanselmann

641 3865ca48 Michael Hanselmann
  This avoids things like /dir/../../other/path to be valid.
642 3865ca48 Michael Hanselmann

643 3865ca48 Michael Hanselmann
  """
644 3865ca48 Michael Hanselmann
  return os.path.normpath(path) == path and os.path.isabs(path)
645 3865ca48 Michael Hanselmann
646 3865ca48 Michael Hanselmann
647 fb17bebd René Nussbaumer
def IsBelowDir(root, other_path):
648 fb17bebd René Nussbaumer
  """Check whether a path is below a root dir.
649 fb17bebd René Nussbaumer

650 2826897c Michael Hanselmann
  This works around the nasty byte-byte comparison of commonprefix.
651 fb17bebd René Nussbaumer

652 fb17bebd René Nussbaumer
  """
653 fb17bebd René Nussbaumer
  if not (os.path.isabs(root) and os.path.isabs(other_path)):
654 fb17bebd René Nussbaumer
    raise ValueError("Provided paths '%s' and '%s' are not absolute" %
655 fb17bebd René Nussbaumer
                     (root, other_path))
656 2826897c Michael Hanselmann
657 2826897c Michael Hanselmann
  norm_other = os.path.normpath(other_path)
658 2826897c Michael Hanselmann
659 2826897c Michael Hanselmann
  if norm_other == os.sep:
660 2826897c Michael Hanselmann
    # The root directory can never be below another path
661 2826897c Michael Hanselmann
    return False
662 2826897c Michael Hanselmann
663 2826897c Michael Hanselmann
  norm_root = os.path.normpath(root)
664 2826897c Michael Hanselmann
665 2826897c Michael Hanselmann
  if norm_root == os.sep:
666 2826897c Michael Hanselmann
    # This is the root directory, no need to add another slash
667 2826897c Michael Hanselmann
    prepared_root = norm_root
668 2826897c Michael Hanselmann
  else:
669 2826897c Michael Hanselmann
    prepared_root = "%s%s" % (norm_root, os.sep)
670 2826897c Michael Hanselmann
671 2826897c Michael Hanselmann
  return os.path.commonprefix([prepared_root, norm_other]) == prepared_root
672 fb17bebd René Nussbaumer
673 fb17bebd René Nussbaumer
674 3865ca48 Michael Hanselmann
def PathJoin(*args):
675 3865ca48 Michael Hanselmann
  """Safe-join a list of path components.
676 3865ca48 Michael Hanselmann

677 3865ca48 Michael Hanselmann
  Requirements:
678 3865ca48 Michael Hanselmann
      - the first argument must be an absolute path
679 3865ca48 Michael Hanselmann
      - no component in the path must have backtracking (e.g. /../),
680 3865ca48 Michael Hanselmann
        since we check for normalization at the end
681 3865ca48 Michael Hanselmann

682 3865ca48 Michael Hanselmann
  @param args: the path components to be joined
683 3865ca48 Michael Hanselmann
  @raise ValueError: for invalid paths
684 3865ca48 Michael Hanselmann

685 3865ca48 Michael Hanselmann
  """
686 3865ca48 Michael Hanselmann
  # ensure we're having at least one path passed in
687 3865ca48 Michael Hanselmann
  assert args
688 3865ca48 Michael Hanselmann
  # ensure the first component is an absolute and normalized path name
689 3865ca48 Michael Hanselmann
  root = args[0]
690 3865ca48 Michael Hanselmann
  if not IsNormAbsPath(root):
691 3865ca48 Michael Hanselmann
    raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
692 3865ca48 Michael Hanselmann
  result = os.path.join(*args)
693 3865ca48 Michael Hanselmann
  # ensure that the whole path is normalized
694 3865ca48 Michael Hanselmann
  if not IsNormAbsPath(result):
695 3865ca48 Michael Hanselmann
    raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
696 3865ca48 Michael Hanselmann
  # check that we're still under the original prefix
697 cf00dba0 René Nussbaumer
  if not IsBelowDir(root, result):
698 3865ca48 Michael Hanselmann
    raise ValueError("Error: path joining resulted in different prefix"
699 cf00dba0 René Nussbaumer
                     " (%s != %s)" % (result, root))
700 3865ca48 Michael Hanselmann
  return result
701 3865ca48 Michael Hanselmann
702 3865ca48 Michael Hanselmann
703 3865ca48 Michael Hanselmann
def TailFile(fname, lines=20):
704 3865ca48 Michael Hanselmann
  """Return the last lines from a file.
705 3865ca48 Michael Hanselmann

706 3865ca48 Michael Hanselmann
  @note: this function will only read and parse the last 4KB of
707 3865ca48 Michael Hanselmann
      the file; if the lines are very long, it could be that less
708 3865ca48 Michael Hanselmann
      than the requested number of lines are returned
709 3865ca48 Michael Hanselmann

710 3865ca48 Michael Hanselmann
  @param fname: the file name
711 3865ca48 Michael Hanselmann
  @type lines: int
712 3865ca48 Michael Hanselmann
  @param lines: the (maximum) number of lines to return
713 3865ca48 Michael Hanselmann

714 3865ca48 Michael Hanselmann
  """
715 3865ca48 Michael Hanselmann
  fd = open(fname, "r")
716 3865ca48 Michael Hanselmann
  try:
717 3865ca48 Michael Hanselmann
    fd.seek(0, 2)
718 3865ca48 Michael Hanselmann
    pos = fd.tell()
719 e687ec01 Michael Hanselmann
    pos = max(0, pos - 4096)
720 3865ca48 Michael Hanselmann
    fd.seek(pos, 0)
721 3865ca48 Michael Hanselmann
    raw_data = fd.read()
722 3865ca48 Michael Hanselmann
  finally:
723 3865ca48 Michael Hanselmann
    fd.close()
724 3865ca48 Michael Hanselmann
725 3865ca48 Michael Hanselmann
  rows = raw_data.splitlines()
726 3865ca48 Michael Hanselmann
  return rows[-lines:]
727 3865ca48 Michael Hanselmann
728 3865ca48 Michael Hanselmann
729 3865ca48 Michael Hanselmann
def BytesToMebibyte(value):
730 3865ca48 Michael Hanselmann
  """Converts bytes to mebibytes.
731 3865ca48 Michael Hanselmann

732 3865ca48 Michael Hanselmann
  @type value: int
733 3865ca48 Michael Hanselmann
  @param value: Value in bytes
734 3865ca48 Michael Hanselmann
  @rtype: int
735 3865ca48 Michael Hanselmann
  @return: Value in mebibytes
736 3865ca48 Michael Hanselmann

737 3865ca48 Michael Hanselmann
  """
738 3865ca48 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
739 3865ca48 Michael Hanselmann
740 3865ca48 Michael Hanselmann
741 3865ca48 Michael Hanselmann
def CalculateDirectorySize(path):
742 3865ca48 Michael Hanselmann
  """Calculates the size of a directory recursively.
743 3865ca48 Michael Hanselmann

744 3865ca48 Michael Hanselmann
  @type path: string
745 3865ca48 Michael Hanselmann
  @param path: Path to directory
746 3865ca48 Michael Hanselmann
  @rtype: int
747 3865ca48 Michael Hanselmann
  @return: Size in mebibytes
748 3865ca48 Michael Hanselmann

749 3865ca48 Michael Hanselmann
  """
750 3865ca48 Michael Hanselmann
  size = 0
751 3865ca48 Michael Hanselmann
752 3865ca48 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
753 3865ca48 Michael Hanselmann
    for filename in files:
754 3865ca48 Michael Hanselmann
      st = os.lstat(PathJoin(curpath, filename))
755 3865ca48 Michael Hanselmann
      size += st.st_size
756 3865ca48 Michael Hanselmann
757 3865ca48 Michael Hanselmann
  return BytesToMebibyte(size)
758 3865ca48 Michael Hanselmann
759 3865ca48 Michael Hanselmann
760 3865ca48 Michael Hanselmann
def GetFilesystemStats(path):
761 3865ca48 Michael Hanselmann
  """Returns the total and free space on a filesystem.
762 3865ca48 Michael Hanselmann

763 3865ca48 Michael Hanselmann
  @type path: string
764 3865ca48 Michael Hanselmann
  @param path: Path on filesystem to be examined
765 3865ca48 Michael Hanselmann
  @rtype: int
766 3865ca48 Michael Hanselmann
  @return: tuple of (Total space, Free space) in mebibytes
767 3865ca48 Michael Hanselmann

768 3865ca48 Michael Hanselmann
  """
769 3865ca48 Michael Hanselmann
  st = os.statvfs(path)
770 3865ca48 Michael Hanselmann
771 3865ca48 Michael Hanselmann
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
772 3865ca48 Michael Hanselmann
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
773 3865ca48 Michael Hanselmann
  return (tsize, fsize)
774 3865ca48 Michael Hanselmann
775 3865ca48 Michael Hanselmann
776 3865ca48 Michael Hanselmann
def ReadPidFile(pidfile):
777 3865ca48 Michael Hanselmann
  """Read a pid from a file.
778 3865ca48 Michael Hanselmann

779 3865ca48 Michael Hanselmann
  @type  pidfile: string
780 3865ca48 Michael Hanselmann
  @param pidfile: path to the file containing the pid
781 3865ca48 Michael Hanselmann
  @rtype: int
782 3865ca48 Michael Hanselmann
  @return: The process id, if the file exists and contains a valid PID,
783 3865ca48 Michael Hanselmann
           otherwise 0
784 3865ca48 Michael Hanselmann

785 3865ca48 Michael Hanselmann
  """
786 3865ca48 Michael Hanselmann
  try:
787 3865ca48 Michael Hanselmann
    raw_data = ReadOneLineFile(pidfile)
788 3865ca48 Michael Hanselmann
  except EnvironmentError, err:
789 3865ca48 Michael Hanselmann
    if err.errno != errno.ENOENT:
790 3865ca48 Michael Hanselmann
      logging.exception("Can't read pid file")
791 3865ca48 Michael Hanselmann
    return 0
792 3865ca48 Michael Hanselmann
793 b6522276 Michael Hanselmann
  return _ParsePidFileContents(raw_data)
794 b6522276 Michael Hanselmann
795 b6522276 Michael Hanselmann
796 b6522276 Michael Hanselmann
def _ParsePidFileContents(data):
797 b6522276 Michael Hanselmann
  """Tries to extract a process ID from a PID file's content.
798 b6522276 Michael Hanselmann

799 b6522276 Michael Hanselmann
  @type data: string
800 b6522276 Michael Hanselmann
  @rtype: int
801 b6522276 Michael Hanselmann
  @return: Zero if nothing could be read, PID otherwise
802 b6522276 Michael Hanselmann

803 b6522276 Michael Hanselmann
  """
804 3865ca48 Michael Hanselmann
  try:
805 b6522276 Michael Hanselmann
    pid = int(data)
806 b6522276 Michael Hanselmann
  except (TypeError, ValueError):
807 3865ca48 Michael Hanselmann
    logging.info("Can't parse pid file contents", exc_info=True)
808 3865ca48 Michael Hanselmann
    return 0
809 b6522276 Michael Hanselmann
  else:
810 b6522276 Michael Hanselmann
    return pid
811 3865ca48 Michael Hanselmann
812 3865ca48 Michael Hanselmann
813 3865ca48 Michael Hanselmann
def ReadLockedPidFile(path):
814 3865ca48 Michael Hanselmann
  """Reads a locked PID file.
815 3865ca48 Michael Hanselmann

816 a4ccecf6 Michael Hanselmann
  This can be used together with L{utils.process.StartDaemon}.
817 3865ca48 Michael Hanselmann

818 3865ca48 Michael Hanselmann
  @type path: string
819 3865ca48 Michael Hanselmann
  @param path: Path to PID file
820 3865ca48 Michael Hanselmann
  @return: PID as integer or, if file was unlocked or couldn't be opened, None
821 3865ca48 Michael Hanselmann

822 3865ca48 Michael Hanselmann
  """
823 3865ca48 Michael Hanselmann
  try:
824 3865ca48 Michael Hanselmann
    fd = os.open(path, os.O_RDONLY)
825 3865ca48 Michael Hanselmann
  except EnvironmentError, err:
826 3865ca48 Michael Hanselmann
    if err.errno == errno.ENOENT:
827 3865ca48 Michael Hanselmann
      # PID file doesn't exist
828 3865ca48 Michael Hanselmann
      return None
829 3865ca48 Michael Hanselmann
    raise
830 3865ca48 Michael Hanselmann
831 3865ca48 Michael Hanselmann
  try:
832 3865ca48 Michael Hanselmann
    try:
833 3865ca48 Michael Hanselmann
      # Try to acquire lock
834 3865ca48 Michael Hanselmann
      filelock.LockFile(fd)
835 3865ca48 Michael Hanselmann
    except errors.LockError:
836 3865ca48 Michael Hanselmann
      # Couldn't lock, daemon is running
837 3865ca48 Michael Hanselmann
      return int(os.read(fd, 100))
838 3865ca48 Michael Hanselmann
  finally:
839 3865ca48 Michael Hanselmann
    os.close(fd)
840 3865ca48 Michael Hanselmann
841 3865ca48 Michael Hanselmann
  return None
842 3865ca48 Michael Hanselmann
843 3865ca48 Michael Hanselmann
844 0232b768 Michael Hanselmann
def _SplitSshKey(key):
845 0232b768 Michael Hanselmann
  """Splits a line for SSH's C{authorized_keys} file.
846 0232b768 Michael Hanselmann

847 0232b768 Michael Hanselmann
  If the line has no options (e.g. no C{command="..."}), only the significant
848 0232b768 Michael Hanselmann
  parts, the key type and its hash, are used. Otherwise the whole line is used
849 0232b768 Michael Hanselmann
  (split at whitespace).
850 0232b768 Michael Hanselmann

851 0232b768 Michael Hanselmann
  @type key: string
852 0232b768 Michael Hanselmann
  @param key: Key line
853 0232b768 Michael Hanselmann
  @rtype: tuple
854 0232b768 Michael Hanselmann

855 0232b768 Michael Hanselmann
  """
856 0232b768 Michael Hanselmann
  parts = key.split()
857 0232b768 Michael Hanselmann
858 d12b9f66 Michael Hanselmann
  if parts and parts[0] in constants.SSHAK_ALL:
859 0232b768 Michael Hanselmann
    # If the key has no options in front of it, we only want the significant
860 0232b768 Michael Hanselmann
    # fields
861 0232b768 Michael Hanselmann
    return (False, parts[:2])
862 0232b768 Michael Hanselmann
  else:
863 0232b768 Michael Hanselmann
    # Can't properly split the line, so use everything
864 0232b768 Michael Hanselmann
    return (True, parts)
865 0232b768 Michael Hanselmann
866 0232b768 Michael Hanselmann
867 3865ca48 Michael Hanselmann
def AddAuthorizedKey(file_obj, key):
868 3865ca48 Michael Hanselmann
  """Adds an SSH public key to an authorized_keys file.
869 3865ca48 Michael Hanselmann

870 3865ca48 Michael Hanselmann
  @type file_obj: str or file handle
871 3865ca48 Michael Hanselmann
  @param file_obj: path to authorized_keys file
872 3865ca48 Michael Hanselmann
  @type key: str
873 3865ca48 Michael Hanselmann
  @param key: string containing key
874 3865ca48 Michael Hanselmann

875 3865ca48 Michael Hanselmann
  """
876 0232b768 Michael Hanselmann
  key_fields = _SplitSshKey(key)
877 3865ca48 Michael Hanselmann
878 3865ca48 Michael Hanselmann
  if isinstance(file_obj, basestring):
879 d0c8c01d Iustin Pop
    f = open(file_obj, "a+")
880 3865ca48 Michael Hanselmann
  else:
881 3865ca48 Michael Hanselmann
    f = file_obj
882 3865ca48 Michael Hanselmann
883 3865ca48 Michael Hanselmann
  try:
884 3865ca48 Michael Hanselmann
    nl = True
885 3865ca48 Michael Hanselmann
    for line in f:
886 3865ca48 Michael Hanselmann
      # Ignore whitespace changes
887 0232b768 Michael Hanselmann
      if _SplitSshKey(line) == key_fields:
888 3865ca48 Michael Hanselmann
        break
889 d0c8c01d Iustin Pop
      nl = line.endswith("\n")
890 3865ca48 Michael Hanselmann
    else:
891 3865ca48 Michael Hanselmann
      if not nl:
892 3865ca48 Michael Hanselmann
        f.write("\n")
893 d0c8c01d Iustin Pop
      f.write(key.rstrip("\r\n"))
894 3865ca48 Michael Hanselmann
      f.write("\n")
895 3865ca48 Michael Hanselmann
      f.flush()
896 3865ca48 Michael Hanselmann
  finally:
897 3865ca48 Michael Hanselmann
    f.close()
898 3865ca48 Michael Hanselmann
899 3865ca48 Michael Hanselmann
900 3865ca48 Michael Hanselmann
def RemoveAuthorizedKey(file_name, key):
901 3865ca48 Michael Hanselmann
  """Removes an SSH public key from an authorized_keys file.
902 3865ca48 Michael Hanselmann

903 3865ca48 Michael Hanselmann
  @type file_name: str
904 3865ca48 Michael Hanselmann
  @param file_name: path to authorized_keys file
905 3865ca48 Michael Hanselmann
  @type key: str
906 3865ca48 Michael Hanselmann
  @param key: string containing key
907 3865ca48 Michael Hanselmann

908 3865ca48 Michael Hanselmann
  """
909 0232b768 Michael Hanselmann
  key_fields = _SplitSshKey(key)
910 3865ca48 Michael Hanselmann
911 3865ca48 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
912 3865ca48 Michael Hanselmann
  try:
913 d0c8c01d Iustin Pop
    out = os.fdopen(fd, "w")
914 3865ca48 Michael Hanselmann
    try:
915 d0c8c01d Iustin Pop
      f = open(file_name, "r")
916 3865ca48 Michael Hanselmann
      try:
917 3865ca48 Michael Hanselmann
        for line in f:
918 3865ca48 Michael Hanselmann
          # Ignore whitespace changes while comparing lines
919 0232b768 Michael Hanselmann
          if _SplitSshKey(line) != key_fields:
920 3865ca48 Michael Hanselmann
            out.write(line)
921 3865ca48 Michael Hanselmann
922 3865ca48 Michael Hanselmann
        out.flush()
923 3865ca48 Michael Hanselmann
        os.rename(tmpname, file_name)
924 3865ca48 Michael Hanselmann
      finally:
925 3865ca48 Michael Hanselmann
        f.close()
926 3865ca48 Michael Hanselmann
    finally:
927 3865ca48 Michael Hanselmann
      out.close()
928 3865ca48 Michael Hanselmann
  except:
929 3865ca48 Michael Hanselmann
    RemoveFile(tmpname)
930 3865ca48 Michael Hanselmann
    raise
931 3865ca48 Michael Hanselmann
932 3865ca48 Michael Hanselmann
933 3865ca48 Michael Hanselmann
def DaemonPidFileName(name):
934 3865ca48 Michael Hanselmann
  """Compute a ganeti pid file absolute path
935 3865ca48 Michael Hanselmann

936 3865ca48 Michael Hanselmann
  @type name: str
937 3865ca48 Michael Hanselmann
  @param name: the daemon name
938 3865ca48 Michael Hanselmann
  @rtype: str
939 3865ca48 Michael Hanselmann
  @return: the full path to the pidfile corresponding to the given
940 3865ca48 Michael Hanselmann
      daemon name
941 3865ca48 Michael Hanselmann

942 3865ca48 Michael Hanselmann
  """
943 111a7d04 Michael Hanselmann
  return PathJoin(pathutils.RUN_DIR, "%s.pid" % name)
944 3865ca48 Michael Hanselmann
945 3865ca48 Michael Hanselmann
946 3865ca48 Michael Hanselmann
def WritePidFile(pidfile):
947 3865ca48 Michael Hanselmann
  """Write the current process pidfile.
948 3865ca48 Michael Hanselmann

949 3865ca48 Michael Hanselmann
  @type pidfile: string
950 3865ca48 Michael Hanselmann
  @param pidfile: the path to the file to be written
951 3865ca48 Michael Hanselmann
  @raise errors.LockError: if the pid file already exists and
952 3865ca48 Michael Hanselmann
      points to a live process
953 3865ca48 Michael Hanselmann
  @rtype: int
954 3865ca48 Michael Hanselmann
  @return: the file descriptor of the lock file; do not close this unless
955 3865ca48 Michael Hanselmann
      you want to unlock the pid file
956 3865ca48 Michael Hanselmann

957 3865ca48 Michael Hanselmann
  """
958 3865ca48 Michael Hanselmann
  # We don't rename nor truncate the file to not drop locks under
959 3865ca48 Michael Hanselmann
  # existing processes
960 b6522276 Michael Hanselmann
  fd_pidfile = os.open(pidfile, os.O_RDWR | os.O_CREAT, 0600)
961 3865ca48 Michael Hanselmann
962 3865ca48 Michael Hanselmann
  # Lock the PID file (and fail if not possible to do so). Any code
963 3865ca48 Michael Hanselmann
  # wanting to send a signal to the daemon should try to lock the PID
964 3865ca48 Michael Hanselmann
  # file before reading it. If acquiring the lock succeeds, the daemon is
965 3865ca48 Michael Hanselmann
  # no longer running and the signal should not be sent.
966 b6522276 Michael Hanselmann
  try:
967 b6522276 Michael Hanselmann
    filelock.LockFile(fd_pidfile)
968 b6522276 Michael Hanselmann
  except errors.LockError:
969 b6522276 Michael Hanselmann
    msg = ["PID file '%s' is already locked by another process" % pidfile]
970 b6522276 Michael Hanselmann
    # Try to read PID file
971 b6522276 Michael Hanselmann
    pid = _ParsePidFileContents(os.read(fd_pidfile, 100))
972 b6522276 Michael Hanselmann
    if pid > 0:
973 b6522276 Michael Hanselmann
      msg.append(", PID read from file is %s" % pid)
974 b6522276 Michael Hanselmann
    raise errors.PidFileLockError("".join(msg))
975 3865ca48 Michael Hanselmann
976 3865ca48 Michael Hanselmann
  os.write(fd_pidfile, "%d\n" % os.getpid())
977 3865ca48 Michael Hanselmann
978 3865ca48 Michael Hanselmann
  return fd_pidfile
979 3865ca48 Michael Hanselmann
980 3865ca48 Michael Hanselmann
981 3865ca48 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
982 3865ca48 Michael Hanselmann
  """Reads the watcher pause file.
983 3865ca48 Michael Hanselmann

984 3865ca48 Michael Hanselmann
  @type filename: string
985 3865ca48 Michael Hanselmann
  @param filename: Path to watcher pause file
986 3865ca48 Michael Hanselmann
  @type now: None, float or int
987 3865ca48 Michael Hanselmann
  @param now: Current time as Unix timestamp
988 3865ca48 Michael Hanselmann
  @type remove_after: int
989 3865ca48 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
990 3865ca48 Michael Hanselmann
    seconds past the pause end time
991 3865ca48 Michael Hanselmann

992 3865ca48 Michael Hanselmann
  """
993 3865ca48 Michael Hanselmann
  if now is None:
994 3865ca48 Michael Hanselmann
    now = time.time()
995 3865ca48 Michael Hanselmann
996 3865ca48 Michael Hanselmann
  try:
997 3865ca48 Michael Hanselmann
    value = ReadFile(filename)
998 3865ca48 Michael Hanselmann
  except IOError, err:
999 3865ca48 Michael Hanselmann
    if err.errno != errno.ENOENT:
1000 3865ca48 Michael Hanselmann
      raise
1001 3865ca48 Michael Hanselmann
    value = None
1002 3865ca48 Michael Hanselmann
1003 3865ca48 Michael Hanselmann
  if value is not None:
1004 3865ca48 Michael Hanselmann
    try:
1005 3865ca48 Michael Hanselmann
      value = int(value)
1006 3865ca48 Michael Hanselmann
    except ValueError:
1007 3865ca48 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
1008 3865ca48 Michael Hanselmann
                       " removing it"), filename)
1009 3865ca48 Michael Hanselmann
      RemoveFile(filename)
1010 3865ca48 Michael Hanselmann
      value = None
1011 3865ca48 Michael Hanselmann
1012 3865ca48 Michael Hanselmann
    if value is not None:
1013 3865ca48 Michael Hanselmann
      # Remove file if it's outdated
1014 3865ca48 Michael Hanselmann
      if now > (value + remove_after):
1015 3865ca48 Michael Hanselmann
        RemoveFile(filename)
1016 3865ca48 Michael Hanselmann
        value = None
1017 3865ca48 Michael Hanselmann
1018 3865ca48 Michael Hanselmann
      elif now > value:
1019 3865ca48 Michael Hanselmann
        value = None
1020 3865ca48 Michael Hanselmann
1021 3865ca48 Michael Hanselmann
  return value
1022 90e234a6 Michael Hanselmann
1023 90e234a6 Michael Hanselmann
1024 90e234a6 Michael Hanselmann
def NewUUID():
1025 90e234a6 Michael Hanselmann
  """Returns a random UUID.
1026 90e234a6 Michael Hanselmann

1027 90e234a6 Michael Hanselmann
  @note: This is a Linux-specific method as it uses the /proc
1028 90e234a6 Michael Hanselmann
      filesystem.
1029 90e234a6 Michael Hanselmann
  @rtype: str
1030 90e234a6 Michael Hanselmann

1031 90e234a6 Michael Hanselmann
  """
1032 90e234a6 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1033 0c1a5b1e Agata Murawska
1034 0c1a5b1e Agata Murawska
1035 0c1a5b1e Agata Murawska
class TemporaryFileManager(object):
1036 0c1a5b1e Agata Murawska
  """Stores the list of files to be deleted and removes them on demand.
1037 0c1a5b1e Agata Murawska

1038 0c1a5b1e Agata Murawska
  """
1039 0c1a5b1e Agata Murawska
1040 0c1a5b1e Agata Murawska
  def __init__(self):
1041 0c1a5b1e Agata Murawska
    self._files = []
1042 0c1a5b1e Agata Murawska
1043 0c1a5b1e Agata Murawska
  def __del__(self):
1044 0c1a5b1e Agata Murawska
    self.Cleanup()
1045 0c1a5b1e Agata Murawska
1046 0c1a5b1e Agata Murawska
  def Add(self, filename):
1047 0c1a5b1e Agata Murawska
    """Add file to list of files to be deleted.
1048 0c1a5b1e Agata Murawska

1049 0c1a5b1e Agata Murawska
    @type filename: string
1050 0c1a5b1e Agata Murawska
    @param filename: path to filename to be added
1051 0c1a5b1e Agata Murawska

1052 0c1a5b1e Agata Murawska
    """
1053 0c1a5b1e Agata Murawska
    self._files.append(filename)
1054 0c1a5b1e Agata Murawska
1055 0c1a5b1e Agata Murawska
  def Remove(self, filename):
1056 0c1a5b1e Agata Murawska
    """Remove file from list of files to be deleted.
1057 0c1a5b1e Agata Murawska

1058 0c1a5b1e Agata Murawska
    @type filename: string
1059 0c1a5b1e Agata Murawska
    @param filename: path to filename to be deleted
1060 0c1a5b1e Agata Murawska

1061 0c1a5b1e Agata Murawska
    """
1062 0c1a5b1e Agata Murawska
    self._files.remove(filename)
1063 0c1a5b1e Agata Murawska
1064 0c1a5b1e Agata Murawska
  def Cleanup(self):
1065 0c1a5b1e Agata Murawska
    """Delete all files marked for deletion
1066 0c1a5b1e Agata Murawska

1067 0c1a5b1e Agata Murawska
    """
1068 0c1a5b1e Agata Murawska
    while self._files:
1069 0c1a5b1e Agata Murawska
      RemoveFile(self._files.pop())