Statistics
| Branch: | Tag: | Revision:

root / lib / utils / io.py @ 823bfa49

History | View | Annotate | Download (20.1 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 823bfa49 Apollon Oikonomopoulos
import re
27 3865ca48 Michael Hanselmann
import logging
28 3865ca48 Michael Hanselmann
import shutil
29 3865ca48 Michael Hanselmann
import tempfile
30 3865ca48 Michael Hanselmann
import errno
31 3865ca48 Michael Hanselmann
import time
32 3865ca48 Michael Hanselmann
33 3865ca48 Michael Hanselmann
from ganeti import errors
34 3865ca48 Michael Hanselmann
from ganeti import constants
35 3865ca48 Michael Hanselmann
from ganeti.utils import filelock
36 3865ca48 Michael Hanselmann
37 3865ca48 Michael Hanselmann
38 90e234a6 Michael Hanselmann
#: Path generating random UUID
39 90e234a6 Michael Hanselmann
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
40 90e234a6 Michael Hanselmann
41 90e234a6 Michael Hanselmann
42 3865ca48 Michael Hanselmann
def ReadFile(file_name, size=-1):
43 3865ca48 Michael Hanselmann
  """Reads a file.
44 3865ca48 Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

441 3865ca48 Michael Hanselmann
  """
442 3865ca48 Michael Hanselmann
  return os.path.normpath(path) == path and os.path.isabs(path)
443 3865ca48 Michael Hanselmann
444 3865ca48 Michael Hanselmann
445 823bfa49 Apollon Oikonomopoulos
def IsNormAbsPathOrURL(path):
446 823bfa49 Apollon Oikonomopoulos
  """Check whether a path is absolute and normalized, or
447 823bfa49 Apollon Oikonomopoulos
  an HTTP URL.
448 823bfa49 Apollon Oikonomopoulos

449 823bfa49 Apollon Oikonomopoulos
  """
450 823bfa49 Apollon Oikonomopoulos
  return IsNormAbsPath(path) or re.match(r'(https?|ftp)://', path)
451 823bfa49 Apollon Oikonomopoulos
452 823bfa49 Apollon Oikonomopoulos
453 3865ca48 Michael Hanselmann
def PathJoin(*args):
454 3865ca48 Michael Hanselmann
  """Safe-join a list of path components.
455 3865ca48 Michael Hanselmann

456 3865ca48 Michael Hanselmann
  Requirements:
457 3865ca48 Michael Hanselmann
      - the first argument must be an absolute path
458 3865ca48 Michael Hanselmann
      - no component in the path must have backtracking (e.g. /../),
459 3865ca48 Michael Hanselmann
        since we check for normalization at the end
460 3865ca48 Michael Hanselmann

461 3865ca48 Michael Hanselmann
  @param args: the path components to be joined
462 3865ca48 Michael Hanselmann
  @raise ValueError: for invalid paths
463 3865ca48 Michael Hanselmann

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

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

490 3865ca48 Michael Hanselmann
  @param fname: the file name
491 3865ca48 Michael Hanselmann
  @type lines: int
492 3865ca48 Michael Hanselmann
  @param lines: the (maximum) number of lines to return
493 3865ca48 Michael Hanselmann

494 3865ca48 Michael Hanselmann
  """
495 3865ca48 Michael Hanselmann
  fd = open(fname, "r")
496 3865ca48 Michael Hanselmann
  try:
497 3865ca48 Michael Hanselmann
    fd.seek(0, 2)
498 3865ca48 Michael Hanselmann
    pos = fd.tell()
499 3865ca48 Michael Hanselmann
    pos = max(0, pos-4096)
500 3865ca48 Michael Hanselmann
    fd.seek(pos, 0)
501 3865ca48 Michael Hanselmann
    raw_data = fd.read()
502 3865ca48 Michael Hanselmann
  finally:
503 3865ca48 Michael Hanselmann
    fd.close()
504 3865ca48 Michael Hanselmann
505 3865ca48 Michael Hanselmann
  rows = raw_data.splitlines()
506 3865ca48 Michael Hanselmann
  return rows[-lines:]
507 3865ca48 Michael Hanselmann
508 3865ca48 Michael Hanselmann
509 3865ca48 Michael Hanselmann
def BytesToMebibyte(value):
510 3865ca48 Michael Hanselmann
  """Converts bytes to mebibytes.
511 3865ca48 Michael Hanselmann

512 3865ca48 Michael Hanselmann
  @type value: int
513 3865ca48 Michael Hanselmann
  @param value: Value in bytes
514 3865ca48 Michael Hanselmann
  @rtype: int
515 3865ca48 Michael Hanselmann
  @return: Value in mebibytes
516 3865ca48 Michael Hanselmann

517 3865ca48 Michael Hanselmann
  """
518 3865ca48 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
519 3865ca48 Michael Hanselmann
520 3865ca48 Michael Hanselmann
521 3865ca48 Michael Hanselmann
def CalculateDirectorySize(path):
522 3865ca48 Michael Hanselmann
  """Calculates the size of a directory recursively.
523 3865ca48 Michael Hanselmann

524 3865ca48 Michael Hanselmann
  @type path: string
525 3865ca48 Michael Hanselmann
  @param path: Path to directory
526 3865ca48 Michael Hanselmann
  @rtype: int
527 3865ca48 Michael Hanselmann
  @return: Size in mebibytes
528 3865ca48 Michael Hanselmann

529 3865ca48 Michael Hanselmann
  """
530 3865ca48 Michael Hanselmann
  size = 0
531 3865ca48 Michael Hanselmann
532 3865ca48 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
533 3865ca48 Michael Hanselmann
    for filename in files:
534 3865ca48 Michael Hanselmann
      st = os.lstat(PathJoin(curpath, filename))
535 3865ca48 Michael Hanselmann
      size += st.st_size
536 3865ca48 Michael Hanselmann
537 3865ca48 Michael Hanselmann
  return BytesToMebibyte(size)
538 3865ca48 Michael Hanselmann
539 3865ca48 Michael Hanselmann
540 3865ca48 Michael Hanselmann
def GetFilesystemStats(path):
541 3865ca48 Michael Hanselmann
  """Returns the total and free space on a filesystem.
542 3865ca48 Michael Hanselmann

543 3865ca48 Michael Hanselmann
  @type path: string
544 3865ca48 Michael Hanselmann
  @param path: Path on filesystem to be examined
545 3865ca48 Michael Hanselmann
  @rtype: int
546 3865ca48 Michael Hanselmann
  @return: tuple of (Total space, Free space) in mebibytes
547 3865ca48 Michael Hanselmann

548 3865ca48 Michael Hanselmann
  """
549 3865ca48 Michael Hanselmann
  st = os.statvfs(path)
550 3865ca48 Michael Hanselmann
551 3865ca48 Michael Hanselmann
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
552 3865ca48 Michael Hanselmann
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
553 3865ca48 Michael Hanselmann
  return (tsize, fsize)
554 3865ca48 Michael Hanselmann
555 3865ca48 Michael Hanselmann
556 3865ca48 Michael Hanselmann
def ReadPidFile(pidfile):
557 3865ca48 Michael Hanselmann
  """Read a pid from a file.
558 3865ca48 Michael Hanselmann

559 3865ca48 Michael Hanselmann
  @type  pidfile: string
560 3865ca48 Michael Hanselmann
  @param pidfile: path to the file containing the pid
561 3865ca48 Michael Hanselmann
  @rtype: int
562 3865ca48 Michael Hanselmann
  @return: The process id, if the file exists and contains a valid PID,
563 3865ca48 Michael Hanselmann
           otherwise 0
564 3865ca48 Michael Hanselmann

565 3865ca48 Michael Hanselmann
  """
566 3865ca48 Michael Hanselmann
  try:
567 3865ca48 Michael Hanselmann
    raw_data = ReadOneLineFile(pidfile)
568 3865ca48 Michael Hanselmann
  except EnvironmentError, err:
569 3865ca48 Michael Hanselmann
    if err.errno != errno.ENOENT:
570 3865ca48 Michael Hanselmann
      logging.exception("Can't read pid file")
571 3865ca48 Michael Hanselmann
    return 0
572 3865ca48 Michael Hanselmann
573 3865ca48 Michael Hanselmann
  try:
574 3865ca48 Michael Hanselmann
    pid = int(raw_data)
575 3865ca48 Michael Hanselmann
  except (TypeError, ValueError), err:
576 3865ca48 Michael Hanselmann
    logging.info("Can't parse pid file contents", exc_info=True)
577 3865ca48 Michael Hanselmann
    return 0
578 3865ca48 Michael Hanselmann
579 3865ca48 Michael Hanselmann
  return pid
580 3865ca48 Michael Hanselmann
581 3865ca48 Michael Hanselmann
582 3865ca48 Michael Hanselmann
def ReadLockedPidFile(path):
583 3865ca48 Michael Hanselmann
  """Reads a locked PID file.
584 3865ca48 Michael Hanselmann

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

587 3865ca48 Michael Hanselmann
  @type path: string
588 3865ca48 Michael Hanselmann
  @param path: Path to PID file
589 3865ca48 Michael Hanselmann
  @return: PID as integer or, if file was unlocked or couldn't be opened, None
590 3865ca48 Michael Hanselmann

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

616 3865ca48 Michael Hanselmann
  @type file_obj: str or file handle
617 3865ca48 Michael Hanselmann
  @param file_obj: path to authorized_keys file
618 3865ca48 Michael Hanselmann
  @type key: str
619 3865ca48 Michael Hanselmann
  @param key: string containing key
620 3865ca48 Michael Hanselmann

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

649 3865ca48 Michael Hanselmann
  @type file_name: str
650 3865ca48 Michael Hanselmann
  @param file_name: path to authorized_keys file
651 3865ca48 Michael Hanselmann
  @type key: str
652 3865ca48 Michael Hanselmann
  @param key: string containing key
653 3865ca48 Michael Hanselmann

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

682 3865ca48 Michael Hanselmann
  @type name: str
683 3865ca48 Michael Hanselmann
  @param name: the daemon name
684 3865ca48 Michael Hanselmann
  @rtype: str
685 3865ca48 Michael Hanselmann
  @return: the full path to the pidfile corresponding to the given
686 3865ca48 Michael Hanselmann
      daemon name
687 3865ca48 Michael Hanselmann

688 3865ca48 Michael Hanselmann
  """
689 3865ca48 Michael Hanselmann
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
690 3865ca48 Michael Hanselmann
691 3865ca48 Michael Hanselmann
692 3865ca48 Michael Hanselmann
def WritePidFile(pidfile):
693 3865ca48 Michael Hanselmann
  """Write the current process pidfile.
694 3865ca48 Michael Hanselmann

695 3865ca48 Michael Hanselmann
  @type pidfile: string
696 3865ca48 Michael Hanselmann
  @param pidfile: the path to the file to be written
697 3865ca48 Michael Hanselmann
  @raise errors.LockError: if the pid file already exists and
698 3865ca48 Michael Hanselmann
      points to a live process
699 3865ca48 Michael Hanselmann
  @rtype: int
700 3865ca48 Michael Hanselmann
  @return: the file descriptor of the lock file; do not close this unless
701 3865ca48 Michael Hanselmann
      you want to unlock the pid file
702 3865ca48 Michael Hanselmann

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

722 3865ca48 Michael Hanselmann
  @type filename: string
723 3865ca48 Michael Hanselmann
  @param filename: Path to watcher pause file
724 3865ca48 Michael Hanselmann
  @type now: None, float or int
725 3865ca48 Michael Hanselmann
  @param now: Current time as Unix timestamp
726 3865ca48 Michael Hanselmann
  @type remove_after: int
727 3865ca48 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
728 3865ca48 Michael Hanselmann
    seconds past the pause end time
729 3865ca48 Michael Hanselmann

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

765 90e234a6 Michael Hanselmann
  @note: This is a Linux-specific method as it uses the /proc
766 90e234a6 Michael Hanselmann
      filesystem.
767 90e234a6 Michael Hanselmann
  @rtype: str
768 90e234a6 Michael Hanselmann

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