Statistics
| Branch: | Tag: | Revision:

root / lib / utils / io.py @ 651cc3e2

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 2dbc6857 Michael Hanselmann
#: Directory used by fsck(8) to store recovered data, usually at a file
39 2dbc6857 Michael Hanselmann
#: system's root directory
40 2dbc6857 Michael Hanselmann
_LOST_AND_FOUND = "lost+found"
41 2dbc6857 Michael Hanselmann
42 c47eddb8 Bernardo Dal Seno
# Possible values for keep_perms in WriteFile()
43 c47eddb8 Bernardo Dal Seno
KP_NEVER = 0
44 c47eddb8 Bernardo Dal Seno
KP_ALWAYS = 1
45 c47eddb8 Bernardo Dal Seno
KP_IF_EXISTS = 2
46 c47eddb8 Bernardo Dal Seno
47 c47eddb8 Bernardo Dal Seno
KEEP_PERMS_VALUES = [
48 c47eddb8 Bernardo Dal Seno
  KP_NEVER,
49 c47eddb8 Bernardo Dal Seno
  KP_ALWAYS,
50 c47eddb8 Bernardo Dal Seno
  KP_IF_EXISTS,
51 c47eddb8 Bernardo Dal Seno
  ]
52 c47eddb8 Bernardo Dal Seno
53 90e234a6 Michael Hanselmann
54 eb93b673 Guido Trotter
def ErrnoOrStr(err):
55 eb93b673 Guido Trotter
  """Format an EnvironmentError exception.
56 eb93b673 Guido Trotter

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

61 eb93b673 Guido Trotter
  @type err: L{EnvironmentError}
62 eb93b673 Guido Trotter
  @param err: the exception to format
63 eb93b673 Guido Trotter

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

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

77 2635bb04 Michael Hanselmann
  """
78 2635bb04 Michael Hanselmann
  def __init__(self):
79 2635bb04 Michael Hanselmann
    """Initializes this class.
80 2635bb04 Michael Hanselmann

81 2635bb04 Michael Hanselmann
    """
82 2635bb04 Michael Hanselmann
    self.st = None
83 2635bb04 Michael Hanselmann
84 2635bb04 Michael Hanselmann
  def __call__(self, fh):
85 2635bb04 Michael Hanselmann
    """Calls C{fstat} on file handle.
86 2635bb04 Michael Hanselmann

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

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

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

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

125 3865ca48 Michael Hanselmann
  If the function doesn't raise an exception, it has succeeded and the
126 3865ca48 Michael Hanselmann
  target file has the new contents. If the function has raised an
127 3865ca48 Michael Hanselmann
  exception, an existing target file should be unmodified and the
128 3865ca48 Michael Hanselmann
  temporary file should be removed.
129 3865ca48 Michael Hanselmann

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

162 3865ca48 Michael Hanselmann
  @rtype: None or int
163 3865ca48 Michael Hanselmann
  @return: None if the 'close' parameter evaluates to True,
164 3865ca48 Michael Hanselmann
      otherwise the file descriptor
165 3865ca48 Michael Hanselmann

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

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

260 3865ca48 Michael Hanselmann
  Either the path to the file or the fd must be given.
261 3865ca48 Michael Hanselmann

262 3865ca48 Michael Hanselmann
  @param path: the file path
263 3865ca48 Michael Hanselmann
  @param fd: a file descriptor
264 3865ca48 Michael Hanselmann
  @return: a tuple of (device number, inode number, mtime)
265 3865ca48 Michael Hanselmann

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

281 3865ca48 Michael Hanselmann
  Differences in the inode/device are not accepted, but and older
282 3865ca48 Michael Hanselmann
  timestamp for fi_disk is accepted.
283 3865ca48 Michael Hanselmann

284 3865ca48 Michael Hanselmann
  @param fi_disk: tuple (dev, inode, mtime) representing the actual
285 3865ca48 Michael Hanselmann
      file data
286 3865ca48 Michael Hanselmann
  @param fi_ours: tuple (dev, inode, mtime) representing the last
287 3865ca48 Michael Hanselmann
      written file data
288 3865ca48 Michael Hanselmann
  @rtype: boolean
289 3865ca48 Michael Hanselmann

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

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

303 3865ca48 Michael Hanselmann
  @type file_name: str
304 3865ca48 Michael Hanselmann
  @param file_name: the target filename
305 3865ca48 Michael Hanselmann
  @type file_id: tuple
306 3865ca48 Michael Hanselmann
  @param file_id: a result from L{GetFileID}
307 3865ca48 Michael Hanselmann

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

325 3865ca48 Michael Hanselmann
  @type strict: boolean
326 3865ca48 Michael Hanselmann
  @param strict: if True, abort if the file has more than one
327 3865ca48 Michael Hanselmann
      non-empty line
328 3865ca48 Michael Hanselmann

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

343 3865ca48 Michael Hanselmann
  Remove a file, ignoring non-existing ones or directories. Other
344 3865ca48 Michael Hanselmann
  errors are passed.
345 3865ca48 Michael Hanselmann

346 3865ca48 Michael Hanselmann
  @type filename: str
347 3865ca48 Michael Hanselmann
  @param filename: the file to be removed
348 3865ca48 Michael Hanselmann

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

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

364 3865ca48 Michael Hanselmann
  @type dirname: str
365 3865ca48 Michael Hanselmann
  @param dirname: the empty directory to be removed
366 3865ca48 Michael Hanselmann

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

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

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

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

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

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

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

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

490 3865ca48 Michael Hanselmann
  This is a wrapper around C{os.makedirs} adding error handling not implemented
491 3865ca48 Michael Hanselmann
  before Python 2.5.
492 3865ca48 Michael Hanselmann

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

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

509 3865ca48 Michael Hanselmann
  """
510 3865ca48 Michael Hanselmann
  return time.strftime("%Y-%m-%d_%H_%M_%S")
511 3865ca48 Michael Hanselmann
512 3865ca48 Michael Hanselmann
513 3865ca48 Michael Hanselmann
def CreateBackup(file_name):
514 3865ca48 Michael Hanselmann
  """Creates a backup of a file.
515 3865ca48 Michael Hanselmann

516 3865ca48 Michael Hanselmann
  @type file_name: str
517 3865ca48 Michael Hanselmann
  @param file_name: file to be backed up
518 3865ca48 Michael Hanselmann
  @rtype: str
519 3865ca48 Michael Hanselmann
  @return: the path to the newly created backup
520 3865ca48 Michael Hanselmann
  @raise errors.ProgrammerError: for invalid file names
521 3865ca48 Michael Hanselmann

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

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

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

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

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

580 3865ca48 Michael Hanselmann
  @param dirs: list of tuples (dir_name, dir_mode)
581 3865ca48 Michael Hanselmann
  @type dirs: list of (string, integer)
582 3865ca48 Michael Hanselmann

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

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

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

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

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

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

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

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

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

678 3865ca48 Michael Hanselmann
  @param args: the path components to be joined
679 3865ca48 Michael Hanselmann
  @raise ValueError: for invalid paths
680 3865ca48 Michael Hanselmann

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

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

706 3865ca48 Michael Hanselmann
  @param fname: the file name
707 3865ca48 Michael Hanselmann
  @type lines: int
708 3865ca48 Michael Hanselmann
  @param lines: the (maximum) number of lines to return
709 3865ca48 Michael Hanselmann

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

728 3865ca48 Michael Hanselmann
  @type value: int
729 3865ca48 Michael Hanselmann
  @param value: Value in bytes
730 3865ca48 Michael Hanselmann
  @rtype: int
731 3865ca48 Michael Hanselmann
  @return: Value in mebibytes
732 3865ca48 Michael Hanselmann

733 3865ca48 Michael Hanselmann
  """
734 3865ca48 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
735 3865ca48 Michael Hanselmann
736 3865ca48 Michael Hanselmann
737 3865ca48 Michael Hanselmann
def CalculateDirectorySize(path):
738 3865ca48 Michael Hanselmann
  """Calculates the size of a directory recursively.
739 3865ca48 Michael Hanselmann

740 3865ca48 Michael Hanselmann
  @type path: string
741 3865ca48 Michael Hanselmann
  @param path: Path to directory
742 3865ca48 Michael Hanselmann
  @rtype: int
743 3865ca48 Michael Hanselmann
  @return: Size in mebibytes
744 3865ca48 Michael Hanselmann

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

759 3865ca48 Michael Hanselmann
  @type path: string
760 3865ca48 Michael Hanselmann
  @param path: Path on filesystem to be examined
761 3865ca48 Michael Hanselmann
  @rtype: int
762 3865ca48 Michael Hanselmann
  @return: tuple of (Total space, Free space) in mebibytes
763 3865ca48 Michael Hanselmann

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

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

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

795 b6522276 Michael Hanselmann
  @type data: string
796 b6522276 Michael Hanselmann
  @rtype: int
797 b6522276 Michael Hanselmann
  @return: Zero if nothing could be read, PID otherwise
798 b6522276 Michael Hanselmann

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

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

814 3865ca48 Michael Hanselmann
  @type path: string
815 3865ca48 Michael Hanselmann
  @param path: Path to PID file
816 3865ca48 Michael Hanselmann
  @return: PID as integer or, if file was unlocked or couldn't be opened, None
817 3865ca48 Michael Hanselmann

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

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

847 0232b768 Michael Hanselmann
  @type key: string
848 0232b768 Michael Hanselmann
  @param key: Key line
849 0232b768 Michael Hanselmann
  @rtype: tuple
850 0232b768 Michael Hanselmann

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

866 3865ca48 Michael Hanselmann
  @type file_obj: str or file handle
867 3865ca48 Michael Hanselmann
  @param file_obj: path to authorized_keys file
868 3865ca48 Michael Hanselmann
  @type key: str
869 3865ca48 Michael Hanselmann
  @param key: string containing key
870 3865ca48 Michael Hanselmann

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

899 3865ca48 Michael Hanselmann
  @type file_name: str
900 3865ca48 Michael Hanselmann
  @param file_name: path to authorized_keys file
901 3865ca48 Michael Hanselmann
  @type key: str
902 3865ca48 Michael Hanselmann
  @param key: string containing key
903 3865ca48 Michael Hanselmann

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

932 3865ca48 Michael Hanselmann
  @type name: str
933 3865ca48 Michael Hanselmann
  @param name: the daemon name
934 3865ca48 Michael Hanselmann
  @rtype: str
935 3865ca48 Michael Hanselmann
  @return: the full path to the pidfile corresponding to the given
936 3865ca48 Michael Hanselmann
      daemon name
937 3865ca48 Michael Hanselmann

938 3865ca48 Michael Hanselmann
  """
939 111a7d04 Michael Hanselmann
  return PathJoin(pathutils.RUN_DIR, "%s.pid" % name)
940 3865ca48 Michael Hanselmann
941 3865ca48 Michael Hanselmann
942 3865ca48 Michael Hanselmann
def WritePidFile(pidfile):
943 3865ca48 Michael Hanselmann
  """Write the current process pidfile.
944 3865ca48 Michael Hanselmann

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

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

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

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

1023 90e234a6 Michael Hanselmann
  @note: This is a Linux-specific method as it uses the /proc
1024 90e234a6 Michael Hanselmann
      filesystem.
1025 90e234a6 Michael Hanselmann
  @rtype: str
1026 90e234a6 Michael Hanselmann

1027 90e234a6 Michael Hanselmann
  """
1028 80a0546b Michele Tartara
  return ReadFile(constants.RANDOM_UUID_FILE, size=128).rstrip("\n")
1029 0c1a5b1e Agata Murawska
1030 0c1a5b1e Agata Murawska
1031 0c1a5b1e Agata Murawska
class TemporaryFileManager(object):
1032 0c1a5b1e Agata Murawska
  """Stores the list of files to be deleted and removes them on demand.
1033 0c1a5b1e Agata Murawska

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

1045 0c1a5b1e Agata Murawska
    @type filename: string
1046 0c1a5b1e Agata Murawska
    @param filename: path to filename to be added
1047 0c1a5b1e Agata Murawska

1048 0c1a5b1e Agata Murawska
    """
1049 0c1a5b1e Agata Murawska
    self._files.append(filename)
1050 0c1a5b1e Agata Murawska
1051 0c1a5b1e Agata Murawska
  def Remove(self, filename):
1052 0c1a5b1e Agata Murawska
    """Remove file from list of files to be deleted.
1053 0c1a5b1e Agata Murawska

1054 0c1a5b1e Agata Murawska
    @type filename: string
1055 0c1a5b1e Agata Murawska
    @param filename: path to filename to be deleted
1056 0c1a5b1e Agata Murawska

1057 0c1a5b1e Agata Murawska
    """
1058 0c1a5b1e Agata Murawska
    self._files.remove(filename)
1059 0c1a5b1e Agata Murawska
1060 0c1a5b1e Agata Murawska
  def Cleanup(self):
1061 0c1a5b1e Agata Murawska
    """Delete all files marked for deletion
1062 0c1a5b1e Agata Murawska

1063 0c1a5b1e Agata Murawska
    """
1064 0c1a5b1e Agata Murawska
    while self._files:
1065 0c1a5b1e Agata Murawska
      RemoveFile(self._files.pop())