Statistics
| Branch: | Tag: | Revision:

root / lib / utils / io.py @ fe698b38

History | View | Annotate | Download (19.9 kB)

1 3865ca48 Michael Hanselmann
#
2 3865ca48 Michael Hanselmann
#
3 3865ca48 Michael Hanselmann
4 3865ca48 Michael Hanselmann
# Copyright (C) 2006, 2007, 2010, 2011 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 3865ca48 Michael Hanselmann
32 3865ca48 Michael Hanselmann
from ganeti import errors
33 3865ca48 Michael Hanselmann
from ganeti import constants
34 3865ca48 Michael Hanselmann
from ganeti.utils import filelock
35 3865ca48 Michael Hanselmann
36 3865ca48 Michael Hanselmann
37 90e234a6 Michael Hanselmann
#: Path generating random UUID
38 90e234a6 Michael Hanselmann
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
39 90e234a6 Michael Hanselmann
40 90e234a6 Michael Hanselmann
41 3865ca48 Michael Hanselmann
def ReadFile(file_name, size=-1):
42 3865ca48 Michael Hanselmann
  """Reads a file.
43 3865ca48 Michael Hanselmann

44 3865ca48 Michael Hanselmann
  @type size: int
45 3865ca48 Michael Hanselmann
  @param size: Read at most size bytes (if negative, entire file)
46 3865ca48 Michael Hanselmann
  @rtype: str
47 3865ca48 Michael Hanselmann
  @return: the (possibly partial) content of the file
48 3865ca48 Michael Hanselmann

49 3865ca48 Michael Hanselmann
  """
50 3865ca48 Michael Hanselmann
  f = open(file_name, "r")
51 3865ca48 Michael Hanselmann
  try:
52 3865ca48 Michael Hanselmann
    return f.read(size)
53 3865ca48 Michael Hanselmann
  finally:
54 3865ca48 Michael Hanselmann
    f.close()
55 3865ca48 Michael Hanselmann
56 3865ca48 Michael Hanselmann
57 3865ca48 Michael Hanselmann
def WriteFile(file_name, fn=None, data=None,
58 3865ca48 Michael Hanselmann
              mode=None, uid=-1, gid=-1,
59 3865ca48 Michael Hanselmann
              atime=None, mtime=None, close=True,
60 3865ca48 Michael Hanselmann
              dry_run=False, backup=False,
61 3865ca48 Michael Hanselmann
              prewrite=None, postwrite=None):
62 3865ca48 Michael Hanselmann
  """(Over)write a file atomically.
63 3865ca48 Michael Hanselmann

64 3865ca48 Michael Hanselmann
  The file_name and either fn (a function taking one argument, the
65 3865ca48 Michael Hanselmann
  file descriptor, and which should write the data to it) or data (the
66 3865ca48 Michael Hanselmann
  contents of the file) must be passed. The other arguments are
67 3865ca48 Michael Hanselmann
  optional and allow setting the file mode, owner and group, and the
68 3865ca48 Michael Hanselmann
  mtime/atime of the file.
69 3865ca48 Michael Hanselmann

70 3865ca48 Michael Hanselmann
  If the function doesn't raise an exception, it has succeeded and the
71 3865ca48 Michael Hanselmann
  target file has the new contents. If the function has raised an
72 3865ca48 Michael Hanselmann
  exception, an existing target file should be unmodified and the
73 3865ca48 Michael Hanselmann
  temporary file should be removed.
74 3865ca48 Michael Hanselmann

75 3865ca48 Michael Hanselmann
  @type file_name: str
76 3865ca48 Michael Hanselmann
  @param file_name: the target filename
77 3865ca48 Michael Hanselmann
  @type fn: callable
78 3865ca48 Michael Hanselmann
  @param fn: content writing function, called with
79 3865ca48 Michael Hanselmann
      file descriptor as parameter
80 3865ca48 Michael Hanselmann
  @type data: str
81 3865ca48 Michael Hanselmann
  @param data: contents of the file
82 3865ca48 Michael Hanselmann
  @type mode: int
83 3865ca48 Michael Hanselmann
  @param mode: file mode
84 3865ca48 Michael Hanselmann
  @type uid: int
85 3865ca48 Michael Hanselmann
  @param uid: the owner of the file
86 3865ca48 Michael Hanselmann
  @type gid: int
87 3865ca48 Michael Hanselmann
  @param gid: the group of the file
88 3865ca48 Michael Hanselmann
  @type atime: int
89 3865ca48 Michael Hanselmann
  @param atime: a custom access time to be set on the file
90 3865ca48 Michael Hanselmann
  @type mtime: int
91 3865ca48 Michael Hanselmann
  @param mtime: a custom modification time to be set on the file
92 3865ca48 Michael Hanselmann
  @type close: boolean
93 3865ca48 Michael Hanselmann
  @param close: whether to close file after writing it
94 3865ca48 Michael Hanselmann
  @type prewrite: callable
95 3865ca48 Michael Hanselmann
  @param prewrite: function to be called before writing content
96 3865ca48 Michael Hanselmann
  @type postwrite: callable
97 3865ca48 Michael Hanselmann
  @param postwrite: function to be called after writing content
98 3865ca48 Michael Hanselmann

99 3865ca48 Michael Hanselmann
  @rtype: None or int
100 3865ca48 Michael Hanselmann
  @return: None if the 'close' parameter evaluates to True,
101 3865ca48 Michael Hanselmann
      otherwise the file descriptor
102 3865ca48 Michael Hanselmann

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

105 3865ca48 Michael Hanselmann
  """
106 3865ca48 Michael Hanselmann
  if not os.path.isabs(file_name):
107 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("Path passed to WriteFile is not"
108 3865ca48 Michael Hanselmann
                                 " absolute: '%s'" % file_name)
109 3865ca48 Michael Hanselmann
110 3865ca48 Michael Hanselmann
  if [fn, data].count(None) != 1:
111 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("fn or data required")
112 3865ca48 Michael Hanselmann
113 3865ca48 Michael Hanselmann
  if [atime, mtime].count(None) == 1:
114 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("Both atime and mtime must be either"
115 3865ca48 Michael Hanselmann
                                 " set or None")
116 3865ca48 Michael Hanselmann
117 3865ca48 Michael Hanselmann
  if backup and not dry_run and os.path.isfile(file_name):
118 3865ca48 Michael Hanselmann
    CreateBackup(file_name)
119 3865ca48 Michael Hanselmann
120 3865ca48 Michael Hanselmann
  dir_name, base_name = os.path.split(file_name)
121 3865ca48 Michael Hanselmann
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
122 3865ca48 Michael Hanselmann
  do_remove = True
123 3865ca48 Michael Hanselmann
  # here we need to make sure we remove the temp file, if any error
124 3865ca48 Michael Hanselmann
  # leaves it in place
125 3865ca48 Michael Hanselmann
  try:
126 3865ca48 Michael Hanselmann
    if uid != -1 or gid != -1:
127 3865ca48 Michael Hanselmann
      os.chown(new_name, uid, gid)
128 3865ca48 Michael Hanselmann
    if mode:
129 3865ca48 Michael Hanselmann
      os.chmod(new_name, mode)
130 3865ca48 Michael Hanselmann
    if callable(prewrite):
131 3865ca48 Michael Hanselmann
      prewrite(fd)
132 3865ca48 Michael Hanselmann
    if data is not None:
133 3865ca48 Michael Hanselmann
      os.write(fd, data)
134 3865ca48 Michael Hanselmann
    else:
135 3865ca48 Michael Hanselmann
      fn(fd)
136 3865ca48 Michael Hanselmann
    if callable(postwrite):
137 3865ca48 Michael Hanselmann
      postwrite(fd)
138 3865ca48 Michael Hanselmann
    os.fsync(fd)
139 3865ca48 Michael Hanselmann
    if atime is not None and mtime is not None:
140 3865ca48 Michael Hanselmann
      os.utime(new_name, (atime, mtime))
141 3865ca48 Michael Hanselmann
    if not dry_run:
142 3865ca48 Michael Hanselmann
      os.rename(new_name, file_name)
143 3865ca48 Michael Hanselmann
      do_remove = False
144 3865ca48 Michael Hanselmann
  finally:
145 3865ca48 Michael Hanselmann
    if close:
146 3865ca48 Michael Hanselmann
      os.close(fd)
147 3865ca48 Michael Hanselmann
      result = None
148 3865ca48 Michael Hanselmann
    else:
149 3865ca48 Michael Hanselmann
      result = fd
150 3865ca48 Michael Hanselmann
    if do_remove:
151 3865ca48 Michael Hanselmann
      RemoveFile(new_name)
152 3865ca48 Michael Hanselmann
153 3865ca48 Michael Hanselmann
  return result
154 3865ca48 Michael Hanselmann
155 3865ca48 Michael Hanselmann
156 3865ca48 Michael Hanselmann
def GetFileID(path=None, fd=None):
157 3865ca48 Michael Hanselmann
  """Returns the file 'id', i.e. the dev/inode and mtime information.
158 3865ca48 Michael Hanselmann

159 3865ca48 Michael Hanselmann
  Either the path to the file or the fd must be given.
160 3865ca48 Michael Hanselmann

161 3865ca48 Michael Hanselmann
  @param path: the file path
162 3865ca48 Michael Hanselmann
  @param fd: a file descriptor
163 3865ca48 Michael Hanselmann
  @return: a tuple of (device number, inode number, mtime)
164 3865ca48 Michael Hanselmann

165 3865ca48 Michael Hanselmann
  """
166 3865ca48 Michael Hanselmann
  if [path, fd].count(None) != 1:
167 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("One and only one of fd/path must be given")
168 3865ca48 Michael Hanselmann
169 3865ca48 Michael Hanselmann
  if fd is None:
170 3865ca48 Michael Hanselmann
    st = os.stat(path)
171 3865ca48 Michael Hanselmann
  else:
172 3865ca48 Michael Hanselmann
    st = os.fstat(fd)
173 3865ca48 Michael Hanselmann
174 3865ca48 Michael Hanselmann
  return (st.st_dev, st.st_ino, st.st_mtime)
175 3865ca48 Michael Hanselmann
176 3865ca48 Michael Hanselmann
177 3865ca48 Michael Hanselmann
def VerifyFileID(fi_disk, fi_ours):
178 3865ca48 Michael Hanselmann
  """Verifies that two file IDs are matching.
179 3865ca48 Michael Hanselmann

180 3865ca48 Michael Hanselmann
  Differences in the inode/device are not accepted, but and older
181 3865ca48 Michael Hanselmann
  timestamp for fi_disk is accepted.
182 3865ca48 Michael Hanselmann

183 3865ca48 Michael Hanselmann
  @param fi_disk: tuple (dev, inode, mtime) representing the actual
184 3865ca48 Michael Hanselmann
      file data
185 3865ca48 Michael Hanselmann
  @param fi_ours: tuple (dev, inode, mtime) representing the last
186 3865ca48 Michael Hanselmann
      written file data
187 3865ca48 Michael Hanselmann
  @rtype: boolean
188 3865ca48 Michael Hanselmann

189 3865ca48 Michael Hanselmann
  """
190 3865ca48 Michael Hanselmann
  (d1, i1, m1) = fi_disk
191 3865ca48 Michael Hanselmann
  (d2, i2, m2) = fi_ours
192 3865ca48 Michael Hanselmann
193 3865ca48 Michael Hanselmann
  return (d1, i1) == (d2, i2) and m1 <= m2
194 3865ca48 Michael Hanselmann
195 3865ca48 Michael Hanselmann
196 3865ca48 Michael Hanselmann
def SafeWriteFile(file_name, file_id, **kwargs):
197 3865ca48 Michael Hanselmann
  """Wraper over L{WriteFile} that locks the target file.
198 3865ca48 Michael Hanselmann

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

202 3865ca48 Michael Hanselmann
  @type file_name: str
203 3865ca48 Michael Hanselmann
  @param file_name: the target filename
204 3865ca48 Michael Hanselmann
  @type file_id: tuple
205 3865ca48 Michael Hanselmann
  @param file_id: a result from L{GetFileID}
206 3865ca48 Michael Hanselmann

207 3865ca48 Michael Hanselmann
  """
208 3865ca48 Michael Hanselmann
  fd = os.open(file_name, os.O_RDONLY | os.O_CREAT)
209 3865ca48 Michael Hanselmann
  try:
210 3865ca48 Michael Hanselmann
    filelock.LockFile(fd)
211 3865ca48 Michael Hanselmann
    if file_id is not None:
212 3865ca48 Michael Hanselmann
      disk_id = GetFileID(fd=fd)
213 3865ca48 Michael Hanselmann
      if not VerifyFileID(disk_id, file_id):
214 3865ca48 Michael Hanselmann
        raise errors.LockError("Cannot overwrite file %s, it has been modified"
215 3865ca48 Michael Hanselmann
                               " since last written" % file_name)
216 3865ca48 Michael Hanselmann
    return WriteFile(file_name, **kwargs)
217 3865ca48 Michael Hanselmann
  finally:
218 3865ca48 Michael Hanselmann
    os.close(fd)
219 3865ca48 Michael Hanselmann
220 3865ca48 Michael Hanselmann
221 3865ca48 Michael Hanselmann
def ReadOneLineFile(file_name, strict=False):
222 3865ca48 Michael Hanselmann
  """Return the first non-empty line from a file.
223 3865ca48 Michael Hanselmann

224 3865ca48 Michael Hanselmann
  @type strict: boolean
225 3865ca48 Michael Hanselmann
  @param strict: if True, abort if the file has more than one
226 3865ca48 Michael Hanselmann
      non-empty line
227 3865ca48 Michael Hanselmann

228 3865ca48 Michael Hanselmann
  """
229 3865ca48 Michael Hanselmann
  file_lines = ReadFile(file_name).splitlines()
230 3865ca48 Michael Hanselmann
  full_lines = filter(bool, file_lines)
231 3865ca48 Michael Hanselmann
  if not file_lines or not full_lines:
232 3865ca48 Michael Hanselmann
    raise errors.GenericError("No data in one-liner file %s" % file_name)
233 3865ca48 Michael Hanselmann
  elif strict and len(full_lines) > 1:
234 3865ca48 Michael Hanselmann
    raise errors.GenericError("Too many lines in one-liner file %s" %
235 3865ca48 Michael Hanselmann
                              file_name)
236 3865ca48 Michael Hanselmann
  return full_lines[0]
237 3865ca48 Michael Hanselmann
238 3865ca48 Michael Hanselmann
239 3865ca48 Michael Hanselmann
def RemoveFile(filename):
240 3865ca48 Michael Hanselmann
  """Remove a file ignoring some errors.
241 3865ca48 Michael Hanselmann

242 3865ca48 Michael Hanselmann
  Remove a file, ignoring non-existing ones or directories. Other
243 3865ca48 Michael Hanselmann
  errors are passed.
244 3865ca48 Michael Hanselmann

245 3865ca48 Michael Hanselmann
  @type filename: str
246 3865ca48 Michael Hanselmann
  @param filename: the file to be removed
247 3865ca48 Michael Hanselmann

248 3865ca48 Michael Hanselmann
  """
249 3865ca48 Michael Hanselmann
  try:
250 3865ca48 Michael Hanselmann
    os.unlink(filename)
251 3865ca48 Michael Hanselmann
  except OSError, err:
252 3865ca48 Michael Hanselmann
    if err.errno not in (errno.ENOENT, errno.EISDIR):
253 3865ca48 Michael Hanselmann
      raise
254 3865ca48 Michael Hanselmann
255 3865ca48 Michael Hanselmann
256 3865ca48 Michael Hanselmann
def RemoveDir(dirname):
257 3865ca48 Michael Hanselmann
  """Remove an empty directory.
258 3865ca48 Michael Hanselmann

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

263 3865ca48 Michael Hanselmann
  @type dirname: str
264 3865ca48 Michael Hanselmann
  @param dirname: the empty directory to be removed
265 3865ca48 Michael Hanselmann

266 3865ca48 Michael Hanselmann
  """
267 3865ca48 Michael Hanselmann
  try:
268 3865ca48 Michael Hanselmann
    os.rmdir(dirname)
269 3865ca48 Michael Hanselmann
  except OSError, err:
270 3865ca48 Michael Hanselmann
    if err.errno != errno.ENOENT:
271 3865ca48 Michael Hanselmann
      raise
272 3865ca48 Michael Hanselmann
273 3865ca48 Michael Hanselmann
274 3865ca48 Michael Hanselmann
def RenameFile(old, new, mkdir=False, mkdir_mode=0750):
275 3865ca48 Michael Hanselmann
  """Renames a file.
276 3865ca48 Michael Hanselmann

277 3865ca48 Michael Hanselmann
  @type old: string
278 3865ca48 Michael Hanselmann
  @param old: Original path
279 3865ca48 Michael Hanselmann
  @type new: string
280 3865ca48 Michael Hanselmann
  @param new: New path
281 3865ca48 Michael Hanselmann
  @type mkdir: bool
282 3865ca48 Michael Hanselmann
  @param mkdir: Whether to create target directory if it doesn't exist
283 3865ca48 Michael Hanselmann
  @type mkdir_mode: int
284 3865ca48 Michael Hanselmann
  @param mkdir_mode: Mode for newly created directories
285 3865ca48 Michael Hanselmann

286 3865ca48 Michael Hanselmann
  """
287 3865ca48 Michael Hanselmann
  try:
288 3865ca48 Michael Hanselmann
    return os.rename(old, new)
289 3865ca48 Michael Hanselmann
  except OSError, err:
290 3865ca48 Michael Hanselmann
    # In at least one use case of this function, the job queue, directory
291 3865ca48 Michael Hanselmann
    # creation is very rare. Checking for the directory before renaming is not
292 3865ca48 Michael Hanselmann
    # as efficient.
293 3865ca48 Michael Hanselmann
    if mkdir and err.errno == errno.ENOENT:
294 3865ca48 Michael Hanselmann
      # Create directory and try again
295 3865ca48 Michael Hanselmann
      Makedirs(os.path.dirname(new), mode=mkdir_mode)
296 3865ca48 Michael Hanselmann
297 3865ca48 Michael Hanselmann
      return os.rename(old, new)
298 3865ca48 Michael Hanselmann
299 3865ca48 Michael Hanselmann
    raise
300 3865ca48 Michael Hanselmann
301 3865ca48 Michael Hanselmann
302 3865ca48 Michael Hanselmann
def Makedirs(path, mode=0750):
303 3865ca48 Michael Hanselmann
  """Super-mkdir; create a leaf directory and all intermediate ones.
304 3865ca48 Michael Hanselmann

305 3865ca48 Michael Hanselmann
  This is a wrapper around C{os.makedirs} adding error handling not implemented
306 3865ca48 Michael Hanselmann
  before Python 2.5.
307 3865ca48 Michael Hanselmann

308 3865ca48 Michael Hanselmann
  """
309 3865ca48 Michael Hanselmann
  try:
310 3865ca48 Michael Hanselmann
    os.makedirs(path, mode)
311 3865ca48 Michael Hanselmann
  except OSError, err:
312 3865ca48 Michael Hanselmann
    # Ignore EEXIST. This is only handled in os.makedirs as included in
313 3865ca48 Michael Hanselmann
    # Python 2.5 and above.
314 3865ca48 Michael Hanselmann
    if err.errno != errno.EEXIST or not os.path.exists(path):
315 3865ca48 Michael Hanselmann
      raise
316 3865ca48 Michael Hanselmann
317 3865ca48 Michael Hanselmann
318 3865ca48 Michael Hanselmann
def TimestampForFilename():
319 3865ca48 Michael Hanselmann
  """Returns the current time formatted for filenames.
320 3865ca48 Michael Hanselmann

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

324 3865ca48 Michael Hanselmann
  """
325 3865ca48 Michael Hanselmann
  return time.strftime("%Y-%m-%d_%H_%M_%S")
326 3865ca48 Michael Hanselmann
327 3865ca48 Michael Hanselmann
328 3865ca48 Michael Hanselmann
def CreateBackup(file_name):
329 3865ca48 Michael Hanselmann
  """Creates a backup of a file.
330 3865ca48 Michael Hanselmann

331 3865ca48 Michael Hanselmann
  @type file_name: str
332 3865ca48 Michael Hanselmann
  @param file_name: file to be backed up
333 3865ca48 Michael Hanselmann
  @rtype: str
334 3865ca48 Michael Hanselmann
  @return: the path to the newly created backup
335 3865ca48 Michael Hanselmann
  @raise errors.ProgrammerError: for invalid file names
336 3865ca48 Michael Hanselmann

337 3865ca48 Michael Hanselmann
  """
338 3865ca48 Michael Hanselmann
  if not os.path.isfile(file_name):
339 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
340 3865ca48 Michael Hanselmann
                                file_name)
341 3865ca48 Michael Hanselmann
342 3865ca48 Michael Hanselmann
  prefix = ("%s.backup-%s." %
343 3865ca48 Michael Hanselmann
            (os.path.basename(file_name), TimestampForFilename()))
344 3865ca48 Michael Hanselmann
  dir_name = os.path.dirname(file_name)
345 3865ca48 Michael Hanselmann
346 3865ca48 Michael Hanselmann
  fsrc = open(file_name, 'rb')
347 3865ca48 Michael Hanselmann
  try:
348 3865ca48 Michael Hanselmann
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
349 3865ca48 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
350 3865ca48 Michael Hanselmann
    try:
351 3865ca48 Michael Hanselmann
      logging.debug("Backing up %s at %s", file_name, backup_name)
352 3865ca48 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
353 3865ca48 Michael Hanselmann
    finally:
354 3865ca48 Michael Hanselmann
      fdst.close()
355 3865ca48 Michael Hanselmann
  finally:
356 3865ca48 Michael Hanselmann
    fsrc.close()
357 3865ca48 Michael Hanselmann
358 3865ca48 Michael Hanselmann
  return backup_name
359 3865ca48 Michael Hanselmann
360 3865ca48 Michael Hanselmann
361 3865ca48 Michael Hanselmann
def ListVisibleFiles(path):
362 3865ca48 Michael Hanselmann
  """Returns a list of visible files in a directory.
363 3865ca48 Michael Hanselmann

364 3865ca48 Michael Hanselmann
  @type path: str
365 3865ca48 Michael Hanselmann
  @param path: the directory to enumerate
366 3865ca48 Michael Hanselmann
  @rtype: list
367 3865ca48 Michael Hanselmann
  @return: the list of all files not starting with a dot
368 3865ca48 Michael Hanselmann
  @raise ProgrammerError: if L{path} is not an absolue and normalized path
369 3865ca48 Michael Hanselmann

370 3865ca48 Michael Hanselmann
  """
371 3865ca48 Michael Hanselmann
  if not IsNormAbsPath(path):
372 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("Path passed to ListVisibleFiles is not"
373 3865ca48 Michael Hanselmann
                                 " absolute/normalized: '%s'" % path)
374 3865ca48 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
375 3865ca48 Michael Hanselmann
  return files
376 3865ca48 Michael Hanselmann
377 3865ca48 Michael Hanselmann
378 3865ca48 Michael Hanselmann
def EnsureDirs(dirs):
379 3865ca48 Michael Hanselmann
  """Make required directories, if they don't exist.
380 3865ca48 Michael Hanselmann

381 3865ca48 Michael Hanselmann
  @param dirs: list of tuples (dir_name, dir_mode)
382 3865ca48 Michael Hanselmann
  @type dirs: list of (string, integer)
383 3865ca48 Michael Hanselmann

384 3865ca48 Michael Hanselmann
  """
385 3865ca48 Michael Hanselmann
  for dir_name, dir_mode in dirs:
386 3865ca48 Michael Hanselmann
    try:
387 3865ca48 Michael Hanselmann
      os.mkdir(dir_name, dir_mode)
388 3865ca48 Michael Hanselmann
    except EnvironmentError, err:
389 3865ca48 Michael Hanselmann
      if err.errno != errno.EEXIST:
390 3865ca48 Michael Hanselmann
        raise errors.GenericError("Cannot create needed directory"
391 3865ca48 Michael Hanselmann
                                  " '%s': %s" % (dir_name, err))
392 3865ca48 Michael Hanselmann
    try:
393 3865ca48 Michael Hanselmann
      os.chmod(dir_name, dir_mode)
394 3865ca48 Michael Hanselmann
    except EnvironmentError, err:
395 3865ca48 Michael Hanselmann
      raise errors.GenericError("Cannot change directory permissions on"
396 3865ca48 Michael Hanselmann
                                " '%s': %s" % (dir_name, err))
397 3865ca48 Michael Hanselmann
    if not os.path.isdir(dir_name):
398 3865ca48 Michael Hanselmann
      raise errors.GenericError("%s is not a directory" % dir_name)
399 3865ca48 Michael Hanselmann
400 3865ca48 Michael Hanselmann
401 3865ca48 Michael Hanselmann
def FindFile(name, search_path, test=os.path.exists):
402 3865ca48 Michael Hanselmann
  """Look for a filesystem object in a given path.
403 3865ca48 Michael Hanselmann

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

407 3865ca48 Michael Hanselmann
  @type name: str
408 3865ca48 Michael Hanselmann
  @param name: the name to look for
409 3865ca48 Michael Hanselmann
  @type search_path: str
410 3865ca48 Michael Hanselmann
  @param search_path: location to start at
411 3865ca48 Michael Hanselmann
  @type test: callable
412 3865ca48 Michael Hanselmann
  @param test: a function taking one argument that should return True
413 3865ca48 Michael Hanselmann
      if the a given object is valid; the default value is
414 3865ca48 Michael Hanselmann
      os.path.exists, causing only existing files to be returned
415 3865ca48 Michael Hanselmann
  @rtype: str or None
416 3865ca48 Michael Hanselmann
  @return: full path to the object if found, None otherwise
417 3865ca48 Michael Hanselmann

418 3865ca48 Michael Hanselmann
  """
419 3865ca48 Michael Hanselmann
  # validate the filename mask
420 3865ca48 Michael Hanselmann
  if constants.EXT_PLUGIN_MASK.match(name) is None:
421 3865ca48 Michael Hanselmann
    logging.critical("Invalid value passed for external script name: '%s'",
422 3865ca48 Michael Hanselmann
                     name)
423 3865ca48 Michael Hanselmann
    return None
424 3865ca48 Michael Hanselmann
425 3865ca48 Michael Hanselmann
  for dir_name in search_path:
426 3865ca48 Michael Hanselmann
    # FIXME: investigate switch to PathJoin
427 3865ca48 Michael Hanselmann
    item_name = os.path.sep.join([dir_name, name])
428 3865ca48 Michael Hanselmann
    # check the user test and that we're indeed resolving to the given
429 3865ca48 Michael Hanselmann
    # basename
430 3865ca48 Michael Hanselmann
    if test(item_name) and os.path.basename(item_name) == name:
431 3865ca48 Michael Hanselmann
      return item_name
432 3865ca48 Michael Hanselmann
  return None
433 3865ca48 Michael Hanselmann
434 3865ca48 Michael Hanselmann
435 3865ca48 Michael Hanselmann
def IsNormAbsPath(path):
436 3865ca48 Michael Hanselmann
  """Check whether a path is absolute and also normalized
437 3865ca48 Michael Hanselmann

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

440 3865ca48 Michael Hanselmann
  """
441 3865ca48 Michael Hanselmann
  return os.path.normpath(path) == path and os.path.isabs(path)
442 3865ca48 Michael Hanselmann
443 3865ca48 Michael Hanselmann
444 3865ca48 Michael Hanselmann
def PathJoin(*args):
445 3865ca48 Michael Hanselmann
  """Safe-join a list of path components.
446 3865ca48 Michael Hanselmann

447 3865ca48 Michael Hanselmann
  Requirements:
448 3865ca48 Michael Hanselmann
      - the first argument must be an absolute path
449 3865ca48 Michael Hanselmann
      - no component in the path must have backtracking (e.g. /../),
450 3865ca48 Michael Hanselmann
        since we check for normalization at the end
451 3865ca48 Michael Hanselmann

452 3865ca48 Michael Hanselmann
  @param args: the path components to be joined
453 3865ca48 Michael Hanselmann
  @raise ValueError: for invalid paths
454 3865ca48 Michael Hanselmann

455 3865ca48 Michael Hanselmann
  """
456 3865ca48 Michael Hanselmann
  # ensure we're having at least one path passed in
457 3865ca48 Michael Hanselmann
  assert args
458 3865ca48 Michael Hanselmann
  # ensure the first component is an absolute and normalized path name
459 3865ca48 Michael Hanselmann
  root = args[0]
460 3865ca48 Michael Hanselmann
  if not IsNormAbsPath(root):
461 3865ca48 Michael Hanselmann
    raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
462 3865ca48 Michael Hanselmann
  result = os.path.join(*args)
463 3865ca48 Michael Hanselmann
  # ensure that the whole path is normalized
464 3865ca48 Michael Hanselmann
  if not IsNormAbsPath(result):
465 3865ca48 Michael Hanselmann
    raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
466 3865ca48 Michael Hanselmann
  # check that we're still under the original prefix
467 3865ca48 Michael Hanselmann
  prefix = os.path.commonprefix([root, result])
468 3865ca48 Michael Hanselmann
  if prefix != root:
469 3865ca48 Michael Hanselmann
    raise ValueError("Error: path joining resulted in different prefix"
470 3865ca48 Michael Hanselmann
                     " (%s != %s)" % (prefix, root))
471 3865ca48 Michael Hanselmann
  return result
472 3865ca48 Michael Hanselmann
473 3865ca48 Michael Hanselmann
474 3865ca48 Michael Hanselmann
def TailFile(fname, lines=20):
475 3865ca48 Michael Hanselmann
  """Return the last lines from a file.
476 3865ca48 Michael Hanselmann

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

481 3865ca48 Michael Hanselmann
  @param fname: the file name
482 3865ca48 Michael Hanselmann
  @type lines: int
483 3865ca48 Michael Hanselmann
  @param lines: the (maximum) number of lines to return
484 3865ca48 Michael Hanselmann

485 3865ca48 Michael Hanselmann
  """
486 3865ca48 Michael Hanselmann
  fd = open(fname, "r")
487 3865ca48 Michael Hanselmann
  try:
488 3865ca48 Michael Hanselmann
    fd.seek(0, 2)
489 3865ca48 Michael Hanselmann
    pos = fd.tell()
490 3865ca48 Michael Hanselmann
    pos = max(0, pos-4096)
491 3865ca48 Michael Hanselmann
    fd.seek(pos, 0)
492 3865ca48 Michael Hanselmann
    raw_data = fd.read()
493 3865ca48 Michael Hanselmann
  finally:
494 3865ca48 Michael Hanselmann
    fd.close()
495 3865ca48 Michael Hanselmann
496 3865ca48 Michael Hanselmann
  rows = raw_data.splitlines()
497 3865ca48 Michael Hanselmann
  return rows[-lines:]
498 3865ca48 Michael Hanselmann
499 3865ca48 Michael Hanselmann
500 3865ca48 Michael Hanselmann
def BytesToMebibyte(value):
501 3865ca48 Michael Hanselmann
  """Converts bytes to mebibytes.
502 3865ca48 Michael Hanselmann

503 3865ca48 Michael Hanselmann
  @type value: int
504 3865ca48 Michael Hanselmann
  @param value: Value in bytes
505 3865ca48 Michael Hanselmann
  @rtype: int
506 3865ca48 Michael Hanselmann
  @return: Value in mebibytes
507 3865ca48 Michael Hanselmann

508 3865ca48 Michael Hanselmann
  """
509 3865ca48 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
510 3865ca48 Michael Hanselmann
511 3865ca48 Michael Hanselmann
512 3865ca48 Michael Hanselmann
def CalculateDirectorySize(path):
513 3865ca48 Michael Hanselmann
  """Calculates the size of a directory recursively.
514 3865ca48 Michael Hanselmann

515 3865ca48 Michael Hanselmann
  @type path: string
516 3865ca48 Michael Hanselmann
  @param path: Path to directory
517 3865ca48 Michael Hanselmann
  @rtype: int
518 3865ca48 Michael Hanselmann
  @return: Size in mebibytes
519 3865ca48 Michael Hanselmann

520 3865ca48 Michael Hanselmann
  """
521 3865ca48 Michael Hanselmann
  size = 0
522 3865ca48 Michael Hanselmann
523 3865ca48 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
524 3865ca48 Michael Hanselmann
    for filename in files:
525 3865ca48 Michael Hanselmann
      st = os.lstat(PathJoin(curpath, filename))
526 3865ca48 Michael Hanselmann
      size += st.st_size
527 3865ca48 Michael Hanselmann
528 3865ca48 Michael Hanselmann
  return BytesToMebibyte(size)
529 3865ca48 Michael Hanselmann
530 3865ca48 Michael Hanselmann
531 3865ca48 Michael Hanselmann
def GetFilesystemStats(path):
532 3865ca48 Michael Hanselmann
  """Returns the total and free space on a filesystem.
533 3865ca48 Michael Hanselmann

534 3865ca48 Michael Hanselmann
  @type path: string
535 3865ca48 Michael Hanselmann
  @param path: Path on filesystem to be examined
536 3865ca48 Michael Hanselmann
  @rtype: int
537 3865ca48 Michael Hanselmann
  @return: tuple of (Total space, Free space) in mebibytes
538 3865ca48 Michael Hanselmann

539 3865ca48 Michael Hanselmann
  """
540 3865ca48 Michael Hanselmann
  st = os.statvfs(path)
541 3865ca48 Michael Hanselmann
542 3865ca48 Michael Hanselmann
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
543 3865ca48 Michael Hanselmann
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
544 3865ca48 Michael Hanselmann
  return (tsize, fsize)
545 3865ca48 Michael Hanselmann
546 3865ca48 Michael Hanselmann
547 3865ca48 Michael Hanselmann
def ReadPidFile(pidfile):
548 3865ca48 Michael Hanselmann
  """Read a pid from a file.
549 3865ca48 Michael Hanselmann

550 3865ca48 Michael Hanselmann
  @type  pidfile: string
551 3865ca48 Michael Hanselmann
  @param pidfile: path to the file containing the pid
552 3865ca48 Michael Hanselmann
  @rtype: int
553 3865ca48 Michael Hanselmann
  @return: The process id, if the file exists and contains a valid PID,
554 3865ca48 Michael Hanselmann
           otherwise 0
555 3865ca48 Michael Hanselmann

556 3865ca48 Michael Hanselmann
  """
557 3865ca48 Michael Hanselmann
  try:
558 3865ca48 Michael Hanselmann
    raw_data = ReadOneLineFile(pidfile)
559 3865ca48 Michael Hanselmann
  except EnvironmentError, err:
560 3865ca48 Michael Hanselmann
    if err.errno != errno.ENOENT:
561 3865ca48 Michael Hanselmann
      logging.exception("Can't read pid file")
562 3865ca48 Michael Hanselmann
    return 0
563 3865ca48 Michael Hanselmann
564 3865ca48 Michael Hanselmann
  try:
565 3865ca48 Michael Hanselmann
    pid = int(raw_data)
566 3865ca48 Michael Hanselmann
  except (TypeError, ValueError), err:
567 3865ca48 Michael Hanselmann
    logging.info("Can't parse pid file contents", exc_info=True)
568 3865ca48 Michael Hanselmann
    return 0
569 3865ca48 Michael Hanselmann
570 3865ca48 Michael Hanselmann
  return pid
571 3865ca48 Michael Hanselmann
572 3865ca48 Michael Hanselmann
573 3865ca48 Michael Hanselmann
def ReadLockedPidFile(path):
574 3865ca48 Michael Hanselmann
  """Reads a locked PID file.
575 3865ca48 Michael Hanselmann

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

578 3865ca48 Michael Hanselmann
  @type path: string
579 3865ca48 Michael Hanselmann
  @param path: Path to PID file
580 3865ca48 Michael Hanselmann
  @return: PID as integer or, if file was unlocked or couldn't be opened, None
581 3865ca48 Michael Hanselmann

582 3865ca48 Michael Hanselmann
  """
583 3865ca48 Michael Hanselmann
  try:
584 3865ca48 Michael Hanselmann
    fd = os.open(path, os.O_RDONLY)
585 3865ca48 Michael Hanselmann
  except EnvironmentError, err:
586 3865ca48 Michael Hanselmann
    if err.errno == errno.ENOENT:
587 3865ca48 Michael Hanselmann
      # PID file doesn't exist
588 3865ca48 Michael Hanselmann
      return None
589 3865ca48 Michael Hanselmann
    raise
590 3865ca48 Michael Hanselmann
591 3865ca48 Michael Hanselmann
  try:
592 3865ca48 Michael Hanselmann
    try:
593 3865ca48 Michael Hanselmann
      # Try to acquire lock
594 3865ca48 Michael Hanselmann
      filelock.LockFile(fd)
595 3865ca48 Michael Hanselmann
    except errors.LockError:
596 3865ca48 Michael Hanselmann
      # Couldn't lock, daemon is running
597 3865ca48 Michael Hanselmann
      return int(os.read(fd, 100))
598 3865ca48 Michael Hanselmann
  finally:
599 3865ca48 Michael Hanselmann
    os.close(fd)
600 3865ca48 Michael Hanselmann
601 3865ca48 Michael Hanselmann
  return None
602 3865ca48 Michael Hanselmann
603 3865ca48 Michael Hanselmann
604 3865ca48 Michael Hanselmann
def AddAuthorizedKey(file_obj, key):
605 3865ca48 Michael Hanselmann
  """Adds an SSH public key to an authorized_keys file.
606 3865ca48 Michael Hanselmann

607 3865ca48 Michael Hanselmann
  @type file_obj: str or file handle
608 3865ca48 Michael Hanselmann
  @param file_obj: path to authorized_keys file
609 3865ca48 Michael Hanselmann
  @type key: str
610 3865ca48 Michael Hanselmann
  @param key: string containing key
611 3865ca48 Michael Hanselmann

612 3865ca48 Michael Hanselmann
  """
613 3865ca48 Michael Hanselmann
  key_fields = key.split()
614 3865ca48 Michael Hanselmann
615 3865ca48 Michael Hanselmann
  if isinstance(file_obj, basestring):
616 3865ca48 Michael Hanselmann
    f = open(file_obj, 'a+')
617 3865ca48 Michael Hanselmann
  else:
618 3865ca48 Michael Hanselmann
    f = file_obj
619 3865ca48 Michael Hanselmann
620 3865ca48 Michael Hanselmann
  try:
621 3865ca48 Michael Hanselmann
    nl = True
622 3865ca48 Michael Hanselmann
    for line in f:
623 3865ca48 Michael Hanselmann
      # Ignore whitespace changes
624 3865ca48 Michael Hanselmann
      if line.split() == key_fields:
625 3865ca48 Michael Hanselmann
        break
626 3865ca48 Michael Hanselmann
      nl = line.endswith('\n')
627 3865ca48 Michael Hanselmann
    else:
628 3865ca48 Michael Hanselmann
      if not nl:
629 3865ca48 Michael Hanselmann
        f.write("\n")
630 3865ca48 Michael Hanselmann
      f.write(key.rstrip('\r\n'))
631 3865ca48 Michael Hanselmann
      f.write("\n")
632 3865ca48 Michael Hanselmann
      f.flush()
633 3865ca48 Michael Hanselmann
  finally:
634 3865ca48 Michael Hanselmann
    f.close()
635 3865ca48 Michael Hanselmann
636 3865ca48 Michael Hanselmann
637 3865ca48 Michael Hanselmann
def RemoveAuthorizedKey(file_name, key):
638 3865ca48 Michael Hanselmann
  """Removes an SSH public key from an authorized_keys file.
639 3865ca48 Michael Hanselmann

640 3865ca48 Michael Hanselmann
  @type file_name: str
641 3865ca48 Michael Hanselmann
  @param file_name: path to authorized_keys file
642 3865ca48 Michael Hanselmann
  @type key: str
643 3865ca48 Michael Hanselmann
  @param key: string containing key
644 3865ca48 Michael Hanselmann

645 3865ca48 Michael Hanselmann
  """
646 3865ca48 Michael Hanselmann
  key_fields = key.split()
647 3865ca48 Michael Hanselmann
648 3865ca48 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
649 3865ca48 Michael Hanselmann
  try:
650 3865ca48 Michael Hanselmann
    out = os.fdopen(fd, 'w')
651 3865ca48 Michael Hanselmann
    try:
652 3865ca48 Michael Hanselmann
      f = open(file_name, 'r')
653 3865ca48 Michael Hanselmann
      try:
654 3865ca48 Michael Hanselmann
        for line in f:
655 3865ca48 Michael Hanselmann
          # Ignore whitespace changes while comparing lines
656 3865ca48 Michael Hanselmann
          if line.split() != key_fields:
657 3865ca48 Michael Hanselmann
            out.write(line)
658 3865ca48 Michael Hanselmann
659 3865ca48 Michael Hanselmann
        out.flush()
660 3865ca48 Michael Hanselmann
        os.rename(tmpname, file_name)
661 3865ca48 Michael Hanselmann
      finally:
662 3865ca48 Michael Hanselmann
        f.close()
663 3865ca48 Michael Hanselmann
    finally:
664 3865ca48 Michael Hanselmann
      out.close()
665 3865ca48 Michael Hanselmann
  except:
666 3865ca48 Michael Hanselmann
    RemoveFile(tmpname)
667 3865ca48 Michael Hanselmann
    raise
668 3865ca48 Michael Hanselmann
669 3865ca48 Michael Hanselmann
670 3865ca48 Michael Hanselmann
def DaemonPidFileName(name):
671 3865ca48 Michael Hanselmann
  """Compute a ganeti pid file absolute path
672 3865ca48 Michael Hanselmann

673 3865ca48 Michael Hanselmann
  @type name: str
674 3865ca48 Michael Hanselmann
  @param name: the daemon name
675 3865ca48 Michael Hanselmann
  @rtype: str
676 3865ca48 Michael Hanselmann
  @return: the full path to the pidfile corresponding to the given
677 3865ca48 Michael Hanselmann
      daemon name
678 3865ca48 Michael Hanselmann

679 3865ca48 Michael Hanselmann
  """
680 3865ca48 Michael Hanselmann
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
681 3865ca48 Michael Hanselmann
682 3865ca48 Michael Hanselmann
683 3865ca48 Michael Hanselmann
def WritePidFile(pidfile):
684 3865ca48 Michael Hanselmann
  """Write the current process pidfile.
685 3865ca48 Michael Hanselmann

686 3865ca48 Michael Hanselmann
  @type pidfile: string
687 3865ca48 Michael Hanselmann
  @param pidfile: the path to the file to be written
688 3865ca48 Michael Hanselmann
  @raise errors.LockError: if the pid file already exists and
689 3865ca48 Michael Hanselmann
      points to a live process
690 3865ca48 Michael Hanselmann
  @rtype: int
691 3865ca48 Michael Hanselmann
  @return: the file descriptor of the lock file; do not close this unless
692 3865ca48 Michael Hanselmann
      you want to unlock the pid file
693 3865ca48 Michael Hanselmann

694 3865ca48 Michael Hanselmann
  """
695 3865ca48 Michael Hanselmann
  # We don't rename nor truncate the file to not drop locks under
696 3865ca48 Michael Hanselmann
  # existing processes
697 3865ca48 Michael Hanselmann
  fd_pidfile = os.open(pidfile, os.O_WRONLY | os.O_CREAT, 0600)
698 3865ca48 Michael Hanselmann
699 3865ca48 Michael Hanselmann
  # Lock the PID file (and fail if not possible to do so). Any code
700 3865ca48 Michael Hanselmann
  # wanting to send a signal to the daemon should try to lock the PID
701 3865ca48 Michael Hanselmann
  # file before reading it. If acquiring the lock succeeds, the daemon is
702 3865ca48 Michael Hanselmann
  # no longer running and the signal should not be sent.
703 3865ca48 Michael Hanselmann
  filelock.LockFile(fd_pidfile)
704 3865ca48 Michael Hanselmann
705 3865ca48 Michael Hanselmann
  os.write(fd_pidfile, "%d\n" % os.getpid())
706 3865ca48 Michael Hanselmann
707 3865ca48 Michael Hanselmann
  return fd_pidfile
708 3865ca48 Michael Hanselmann
709 3865ca48 Michael Hanselmann
710 3865ca48 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
711 3865ca48 Michael Hanselmann
  """Reads the watcher pause file.
712 3865ca48 Michael Hanselmann

713 3865ca48 Michael Hanselmann
  @type filename: string
714 3865ca48 Michael Hanselmann
  @param filename: Path to watcher pause file
715 3865ca48 Michael Hanselmann
  @type now: None, float or int
716 3865ca48 Michael Hanselmann
  @param now: Current time as Unix timestamp
717 3865ca48 Michael Hanselmann
  @type remove_after: int
718 3865ca48 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
719 3865ca48 Michael Hanselmann
    seconds past the pause end time
720 3865ca48 Michael Hanselmann

721 3865ca48 Michael Hanselmann
  """
722 3865ca48 Michael Hanselmann
  if now is None:
723 3865ca48 Michael Hanselmann
    now = time.time()
724 3865ca48 Michael Hanselmann
725 3865ca48 Michael Hanselmann
  try:
726 3865ca48 Michael Hanselmann
    value = ReadFile(filename)
727 3865ca48 Michael Hanselmann
  except IOError, err:
728 3865ca48 Michael Hanselmann
    if err.errno != errno.ENOENT:
729 3865ca48 Michael Hanselmann
      raise
730 3865ca48 Michael Hanselmann
    value = None
731 3865ca48 Michael Hanselmann
732 3865ca48 Michael Hanselmann
  if value is not None:
733 3865ca48 Michael Hanselmann
    try:
734 3865ca48 Michael Hanselmann
      value = int(value)
735 3865ca48 Michael Hanselmann
    except ValueError:
736 3865ca48 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
737 3865ca48 Michael Hanselmann
                       " removing it"), filename)
738 3865ca48 Michael Hanselmann
      RemoveFile(filename)
739 3865ca48 Michael Hanselmann
      value = None
740 3865ca48 Michael Hanselmann
741 3865ca48 Michael Hanselmann
    if value is not None:
742 3865ca48 Michael Hanselmann
      # Remove file if it's outdated
743 3865ca48 Michael Hanselmann
      if now > (value + remove_after):
744 3865ca48 Michael Hanselmann
        RemoveFile(filename)
745 3865ca48 Michael Hanselmann
        value = None
746 3865ca48 Michael Hanselmann
747 3865ca48 Michael Hanselmann
      elif now > value:
748 3865ca48 Michael Hanselmann
        value = None
749 3865ca48 Michael Hanselmann
750 3865ca48 Michael Hanselmann
  return value
751 90e234a6 Michael Hanselmann
752 90e234a6 Michael Hanselmann
753 90e234a6 Michael Hanselmann
def NewUUID():
754 90e234a6 Michael Hanselmann
  """Returns a random UUID.
755 90e234a6 Michael Hanselmann

756 90e234a6 Michael Hanselmann
  @note: This is a Linux-specific method as it uses the /proc
757 90e234a6 Michael Hanselmann
      filesystem.
758 90e234a6 Michael Hanselmann
  @rtype: str
759 90e234a6 Michael Hanselmann

760 90e234a6 Michael Hanselmann
  """
761 90e234a6 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")