Statistics
| Branch: | Tag: | Revision:

root / lib / utils / io.py @ 638ac34b

History | View | Annotate | Download (20.5 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 a9d68e40 Michael Hanselmann
  # Whether temporary file needs to be removed (e.g. if any error occurs)
121 3865ca48 Michael Hanselmann
  do_remove = True
122 a9d68e40 Michael Hanselmann
123 a9d68e40 Michael Hanselmann
  # Function result
124 a9d68e40 Michael Hanselmann
  result = None
125 a9d68e40 Michael Hanselmann
126 a9d68e40 Michael Hanselmann
  (dir_name, base_name) = os.path.split(file_name)
127 a9d68e40 Michael Hanselmann
  (fd, new_name) = tempfile.mkstemp(suffix=".new", prefix=base_name,
128 a9d68e40 Michael Hanselmann
                                    dir=dir_name)
129 3865ca48 Michael Hanselmann
  try:
130 a9d68e40 Michael Hanselmann
    try:
131 a9d68e40 Michael Hanselmann
      if uid != -1 or gid != -1:
132 a9d68e40 Michael Hanselmann
        os.chown(new_name, uid, gid)
133 a9d68e40 Michael Hanselmann
      if mode:
134 a9d68e40 Michael Hanselmann
        os.chmod(new_name, mode)
135 a9d68e40 Michael Hanselmann
      if callable(prewrite):
136 a9d68e40 Michael Hanselmann
        prewrite(fd)
137 a9d68e40 Michael Hanselmann
      if data is not None:
138 1d39e245 Iustin Pop
        if isinstance(data, unicode):
139 1d39e245 Iustin Pop
          data = data.encode()
140 1d39e245 Iustin Pop
        assert isinstance(data, str)
141 437c3e77 Iustin Pop
        to_write = len(data)
142 437c3e77 Iustin Pop
        offset = 0
143 437c3e77 Iustin Pop
        while offset < to_write:
144 437c3e77 Iustin Pop
          written = os.write(fd, buffer(data, offset))
145 437c3e77 Iustin Pop
          assert written >= 0
146 1d39e245 Iustin Pop
          assert written <= to_write - offset
147 437c3e77 Iustin Pop
          offset += written
148 437c3e77 Iustin Pop
        assert offset == to_write
149 a9d68e40 Michael Hanselmann
      else:
150 a9d68e40 Michael Hanselmann
        fn(fd)
151 a9d68e40 Michael Hanselmann
      if callable(postwrite):
152 a9d68e40 Michael Hanselmann
        postwrite(fd)
153 a9d68e40 Michael Hanselmann
      os.fsync(fd)
154 a9d68e40 Michael Hanselmann
      if atime is not None and mtime is not None:
155 a9d68e40 Michael Hanselmann
        os.utime(new_name, (atime, mtime))
156 a9d68e40 Michael Hanselmann
    finally:
157 a9d68e40 Michael Hanselmann
      # Close file unless the file descriptor should be returned
158 a9d68e40 Michael Hanselmann
      if close:
159 a9d68e40 Michael Hanselmann
        os.close(fd)
160 a9d68e40 Michael Hanselmann
      else:
161 a9d68e40 Michael Hanselmann
        result = fd
162 a9d68e40 Michael Hanselmann
163 a9d68e40 Michael Hanselmann
    # Rename file to destination name
164 3865ca48 Michael Hanselmann
    if not dry_run:
165 3865ca48 Michael Hanselmann
      os.rename(new_name, file_name)
166 a9d68e40 Michael Hanselmann
      # Successful, no need to remove anymore
167 3865ca48 Michael Hanselmann
      do_remove = False
168 3865ca48 Michael Hanselmann
  finally:
169 3865ca48 Michael Hanselmann
    if do_remove:
170 3865ca48 Michael Hanselmann
      RemoveFile(new_name)
171 3865ca48 Michael Hanselmann
172 3865ca48 Michael Hanselmann
  return result
173 3865ca48 Michael Hanselmann
174 3865ca48 Michael Hanselmann
175 3865ca48 Michael Hanselmann
def GetFileID(path=None, fd=None):
176 3865ca48 Michael Hanselmann
  """Returns the file 'id', i.e. the dev/inode and mtime information.
177 3865ca48 Michael Hanselmann

178 3865ca48 Michael Hanselmann
  Either the path to the file or the fd must be given.
179 3865ca48 Michael Hanselmann

180 3865ca48 Michael Hanselmann
  @param path: the file path
181 3865ca48 Michael Hanselmann
  @param fd: a file descriptor
182 3865ca48 Michael Hanselmann
  @return: a tuple of (device number, inode number, mtime)
183 3865ca48 Michael Hanselmann

184 3865ca48 Michael Hanselmann
  """
185 3865ca48 Michael Hanselmann
  if [path, fd].count(None) != 1:
186 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("One and only one of fd/path must be given")
187 3865ca48 Michael Hanselmann
188 3865ca48 Michael Hanselmann
  if fd is None:
189 3865ca48 Michael Hanselmann
    st = os.stat(path)
190 3865ca48 Michael Hanselmann
  else:
191 3865ca48 Michael Hanselmann
    st = os.fstat(fd)
192 3865ca48 Michael Hanselmann
193 3865ca48 Michael Hanselmann
  return (st.st_dev, st.st_ino, st.st_mtime)
194 3865ca48 Michael Hanselmann
195 3865ca48 Michael Hanselmann
196 3865ca48 Michael Hanselmann
def VerifyFileID(fi_disk, fi_ours):
197 3865ca48 Michael Hanselmann
  """Verifies that two file IDs are matching.
198 3865ca48 Michael Hanselmann

199 3865ca48 Michael Hanselmann
  Differences in the inode/device are not accepted, but and older
200 3865ca48 Michael Hanselmann
  timestamp for fi_disk is accepted.
201 3865ca48 Michael Hanselmann

202 3865ca48 Michael Hanselmann
  @param fi_disk: tuple (dev, inode, mtime) representing the actual
203 3865ca48 Michael Hanselmann
      file data
204 3865ca48 Michael Hanselmann
  @param fi_ours: tuple (dev, inode, mtime) representing the last
205 3865ca48 Michael Hanselmann
      written file data
206 3865ca48 Michael Hanselmann
  @rtype: boolean
207 3865ca48 Michael Hanselmann

208 3865ca48 Michael Hanselmann
  """
209 3865ca48 Michael Hanselmann
  (d1, i1, m1) = fi_disk
210 3865ca48 Michael Hanselmann
  (d2, i2, m2) = fi_ours
211 3865ca48 Michael Hanselmann
212 3865ca48 Michael Hanselmann
  return (d1, i1) == (d2, i2) and m1 <= m2
213 3865ca48 Michael Hanselmann
214 3865ca48 Michael Hanselmann
215 3865ca48 Michael Hanselmann
def SafeWriteFile(file_name, file_id, **kwargs):
216 3865ca48 Michael Hanselmann
  """Wraper over L{WriteFile} that locks the target file.
217 3865ca48 Michael Hanselmann

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

221 3865ca48 Michael Hanselmann
  @type file_name: str
222 3865ca48 Michael Hanselmann
  @param file_name: the target filename
223 3865ca48 Michael Hanselmann
  @type file_id: tuple
224 3865ca48 Michael Hanselmann
  @param file_id: a result from L{GetFileID}
225 3865ca48 Michael Hanselmann

226 3865ca48 Michael Hanselmann
  """
227 3865ca48 Michael Hanselmann
  fd = os.open(file_name, os.O_RDONLY | os.O_CREAT)
228 3865ca48 Michael Hanselmann
  try:
229 3865ca48 Michael Hanselmann
    filelock.LockFile(fd)
230 3865ca48 Michael Hanselmann
    if file_id is not None:
231 3865ca48 Michael Hanselmann
      disk_id = GetFileID(fd=fd)
232 3865ca48 Michael Hanselmann
      if not VerifyFileID(disk_id, file_id):
233 3865ca48 Michael Hanselmann
        raise errors.LockError("Cannot overwrite file %s, it has been modified"
234 3865ca48 Michael Hanselmann
                               " since last written" % file_name)
235 3865ca48 Michael Hanselmann
    return WriteFile(file_name, **kwargs)
236 3865ca48 Michael Hanselmann
  finally:
237 3865ca48 Michael Hanselmann
    os.close(fd)
238 3865ca48 Michael Hanselmann
239 3865ca48 Michael Hanselmann
240 3865ca48 Michael Hanselmann
def ReadOneLineFile(file_name, strict=False):
241 3865ca48 Michael Hanselmann
  """Return the first non-empty line from a file.
242 3865ca48 Michael Hanselmann

243 3865ca48 Michael Hanselmann
  @type strict: boolean
244 3865ca48 Michael Hanselmann
  @param strict: if True, abort if the file has more than one
245 3865ca48 Michael Hanselmann
      non-empty line
246 3865ca48 Michael Hanselmann

247 3865ca48 Michael Hanselmann
  """
248 3865ca48 Michael Hanselmann
  file_lines = ReadFile(file_name).splitlines()
249 3865ca48 Michael Hanselmann
  full_lines = filter(bool, file_lines)
250 3865ca48 Michael Hanselmann
  if not file_lines or not full_lines:
251 3865ca48 Michael Hanselmann
    raise errors.GenericError("No data in one-liner file %s" % file_name)
252 3865ca48 Michael Hanselmann
  elif strict and len(full_lines) > 1:
253 3865ca48 Michael Hanselmann
    raise errors.GenericError("Too many lines in one-liner file %s" %
254 3865ca48 Michael Hanselmann
                              file_name)
255 3865ca48 Michael Hanselmann
  return full_lines[0]
256 3865ca48 Michael Hanselmann
257 3865ca48 Michael Hanselmann
258 3865ca48 Michael Hanselmann
def RemoveFile(filename):
259 3865ca48 Michael Hanselmann
  """Remove a file ignoring some errors.
260 3865ca48 Michael Hanselmann

261 3865ca48 Michael Hanselmann
  Remove a file, ignoring non-existing ones or directories. Other
262 3865ca48 Michael Hanselmann
  errors are passed.
263 3865ca48 Michael Hanselmann

264 3865ca48 Michael Hanselmann
  @type filename: str
265 3865ca48 Michael Hanselmann
  @param filename: the file to be removed
266 3865ca48 Michael Hanselmann

267 3865ca48 Michael Hanselmann
  """
268 3865ca48 Michael Hanselmann
  try:
269 3865ca48 Michael Hanselmann
    os.unlink(filename)
270 3865ca48 Michael Hanselmann
  except OSError, err:
271 3865ca48 Michael Hanselmann
    if err.errno not in (errno.ENOENT, errno.EISDIR):
272 3865ca48 Michael Hanselmann
      raise
273 3865ca48 Michael Hanselmann
274 3865ca48 Michael Hanselmann
275 3865ca48 Michael Hanselmann
def RemoveDir(dirname):
276 3865ca48 Michael Hanselmann
  """Remove an empty directory.
277 3865ca48 Michael Hanselmann

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

282 3865ca48 Michael Hanselmann
  @type dirname: str
283 3865ca48 Michael Hanselmann
  @param dirname: the empty directory to be removed
284 3865ca48 Michael Hanselmann

285 3865ca48 Michael Hanselmann
  """
286 3865ca48 Michael Hanselmann
  try:
287 3865ca48 Michael Hanselmann
    os.rmdir(dirname)
288 3865ca48 Michael Hanselmann
  except OSError, err:
289 3865ca48 Michael Hanselmann
    if err.errno != errno.ENOENT:
290 3865ca48 Michael Hanselmann
      raise
291 3865ca48 Michael Hanselmann
292 3865ca48 Michael Hanselmann
293 3865ca48 Michael Hanselmann
def RenameFile(old, new, mkdir=False, mkdir_mode=0750):
294 3865ca48 Michael Hanselmann
  """Renames a file.
295 3865ca48 Michael Hanselmann

296 3865ca48 Michael Hanselmann
  @type old: string
297 3865ca48 Michael Hanselmann
  @param old: Original path
298 3865ca48 Michael Hanselmann
  @type new: string
299 3865ca48 Michael Hanselmann
  @param new: New path
300 3865ca48 Michael Hanselmann
  @type mkdir: bool
301 3865ca48 Michael Hanselmann
  @param mkdir: Whether to create target directory if it doesn't exist
302 3865ca48 Michael Hanselmann
  @type mkdir_mode: int
303 3865ca48 Michael Hanselmann
  @param mkdir_mode: Mode for newly created directories
304 3865ca48 Michael Hanselmann

305 3865ca48 Michael Hanselmann
  """
306 3865ca48 Michael Hanselmann
  try:
307 3865ca48 Michael Hanselmann
    return os.rename(old, new)
308 3865ca48 Michael Hanselmann
  except OSError, err:
309 3865ca48 Michael Hanselmann
    # In at least one use case of this function, the job queue, directory
310 3865ca48 Michael Hanselmann
    # creation is very rare. Checking for the directory before renaming is not
311 3865ca48 Michael Hanselmann
    # as efficient.
312 3865ca48 Michael Hanselmann
    if mkdir and err.errno == errno.ENOENT:
313 3865ca48 Michael Hanselmann
      # Create directory and try again
314 3865ca48 Michael Hanselmann
      Makedirs(os.path.dirname(new), mode=mkdir_mode)
315 3865ca48 Michael Hanselmann
316 3865ca48 Michael Hanselmann
      return os.rename(old, new)
317 3865ca48 Michael Hanselmann
318 3865ca48 Michael Hanselmann
    raise
319 3865ca48 Michael Hanselmann
320 3865ca48 Michael Hanselmann
321 3865ca48 Michael Hanselmann
def Makedirs(path, mode=0750):
322 3865ca48 Michael Hanselmann
  """Super-mkdir; create a leaf directory and all intermediate ones.
323 3865ca48 Michael Hanselmann

324 3865ca48 Michael Hanselmann
  This is a wrapper around C{os.makedirs} adding error handling not implemented
325 3865ca48 Michael Hanselmann
  before Python 2.5.
326 3865ca48 Michael Hanselmann

327 3865ca48 Michael Hanselmann
  """
328 3865ca48 Michael Hanselmann
  try:
329 3865ca48 Michael Hanselmann
    os.makedirs(path, mode)
330 3865ca48 Michael Hanselmann
  except OSError, err:
331 3865ca48 Michael Hanselmann
    # Ignore EEXIST. This is only handled in os.makedirs as included in
332 3865ca48 Michael Hanselmann
    # Python 2.5 and above.
333 3865ca48 Michael Hanselmann
    if err.errno != errno.EEXIST or not os.path.exists(path):
334 3865ca48 Michael Hanselmann
      raise
335 3865ca48 Michael Hanselmann
336 3865ca48 Michael Hanselmann
337 3865ca48 Michael Hanselmann
def TimestampForFilename():
338 3865ca48 Michael Hanselmann
  """Returns the current time formatted for filenames.
339 3865ca48 Michael Hanselmann

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

343 3865ca48 Michael Hanselmann
  """
344 3865ca48 Michael Hanselmann
  return time.strftime("%Y-%m-%d_%H_%M_%S")
345 3865ca48 Michael Hanselmann
346 3865ca48 Michael Hanselmann
347 3865ca48 Michael Hanselmann
def CreateBackup(file_name):
348 3865ca48 Michael Hanselmann
  """Creates a backup of a file.
349 3865ca48 Michael Hanselmann

350 3865ca48 Michael Hanselmann
  @type file_name: str
351 3865ca48 Michael Hanselmann
  @param file_name: file to be backed up
352 3865ca48 Michael Hanselmann
  @rtype: str
353 3865ca48 Michael Hanselmann
  @return: the path to the newly created backup
354 3865ca48 Michael Hanselmann
  @raise errors.ProgrammerError: for invalid file names
355 3865ca48 Michael Hanselmann

356 3865ca48 Michael Hanselmann
  """
357 3865ca48 Michael Hanselmann
  if not os.path.isfile(file_name):
358 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
359 3865ca48 Michael Hanselmann
                                file_name)
360 3865ca48 Michael Hanselmann
361 3865ca48 Michael Hanselmann
  prefix = ("%s.backup-%s." %
362 3865ca48 Michael Hanselmann
            (os.path.basename(file_name), TimestampForFilename()))
363 3865ca48 Michael Hanselmann
  dir_name = os.path.dirname(file_name)
364 3865ca48 Michael Hanselmann
365 3865ca48 Michael Hanselmann
  fsrc = open(file_name, 'rb')
366 3865ca48 Michael Hanselmann
  try:
367 3865ca48 Michael Hanselmann
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
368 3865ca48 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
369 3865ca48 Michael Hanselmann
    try:
370 3865ca48 Michael Hanselmann
      logging.debug("Backing up %s at %s", file_name, backup_name)
371 3865ca48 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
372 3865ca48 Michael Hanselmann
    finally:
373 3865ca48 Michael Hanselmann
      fdst.close()
374 3865ca48 Michael Hanselmann
  finally:
375 3865ca48 Michael Hanselmann
    fsrc.close()
376 3865ca48 Michael Hanselmann
377 3865ca48 Michael Hanselmann
  return backup_name
378 3865ca48 Michael Hanselmann
379 3865ca48 Michael Hanselmann
380 3865ca48 Michael Hanselmann
def ListVisibleFiles(path):
381 3865ca48 Michael Hanselmann
  """Returns a list of visible files in a directory.
382 3865ca48 Michael Hanselmann

383 3865ca48 Michael Hanselmann
  @type path: str
384 3865ca48 Michael Hanselmann
  @param path: the directory to enumerate
385 3865ca48 Michael Hanselmann
  @rtype: list
386 3865ca48 Michael Hanselmann
  @return: the list of all files not starting with a dot
387 3865ca48 Michael Hanselmann
  @raise ProgrammerError: if L{path} is not an absolue and normalized path
388 3865ca48 Michael Hanselmann

389 3865ca48 Michael Hanselmann
  """
390 3865ca48 Michael Hanselmann
  if not IsNormAbsPath(path):
391 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("Path passed to ListVisibleFiles is not"
392 3865ca48 Michael Hanselmann
                                 " absolute/normalized: '%s'" % path)
393 3865ca48 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
394 3865ca48 Michael Hanselmann
  return files
395 3865ca48 Michael Hanselmann
396 3865ca48 Michael Hanselmann
397 3865ca48 Michael Hanselmann
def EnsureDirs(dirs):
398 3865ca48 Michael Hanselmann
  """Make required directories, if they don't exist.
399 3865ca48 Michael Hanselmann

400 3865ca48 Michael Hanselmann
  @param dirs: list of tuples (dir_name, dir_mode)
401 3865ca48 Michael Hanselmann
  @type dirs: list of (string, integer)
402 3865ca48 Michael Hanselmann

403 3865ca48 Michael Hanselmann
  """
404 3865ca48 Michael Hanselmann
  for dir_name, dir_mode in dirs:
405 3865ca48 Michael Hanselmann
    try:
406 3865ca48 Michael Hanselmann
      os.mkdir(dir_name, dir_mode)
407 3865ca48 Michael Hanselmann
    except EnvironmentError, err:
408 3865ca48 Michael Hanselmann
      if err.errno != errno.EEXIST:
409 3865ca48 Michael Hanselmann
        raise errors.GenericError("Cannot create needed directory"
410 3865ca48 Michael Hanselmann
                                  " '%s': %s" % (dir_name, err))
411 3865ca48 Michael Hanselmann
    try:
412 3865ca48 Michael Hanselmann
      os.chmod(dir_name, dir_mode)
413 3865ca48 Michael Hanselmann
    except EnvironmentError, err:
414 3865ca48 Michael Hanselmann
      raise errors.GenericError("Cannot change directory permissions on"
415 3865ca48 Michael Hanselmann
                                " '%s': %s" % (dir_name, err))
416 3865ca48 Michael Hanselmann
    if not os.path.isdir(dir_name):
417 3865ca48 Michael Hanselmann
      raise errors.GenericError("%s is not a directory" % dir_name)
418 3865ca48 Michael Hanselmann
419 3865ca48 Michael Hanselmann
420 3865ca48 Michael Hanselmann
def FindFile(name, search_path, test=os.path.exists):
421 3865ca48 Michael Hanselmann
  """Look for a filesystem object in a given path.
422 3865ca48 Michael Hanselmann

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

426 3865ca48 Michael Hanselmann
  @type name: str
427 3865ca48 Michael Hanselmann
  @param name: the name to look for
428 3865ca48 Michael Hanselmann
  @type search_path: str
429 3865ca48 Michael Hanselmann
  @param search_path: location to start at
430 3865ca48 Michael Hanselmann
  @type test: callable
431 3865ca48 Michael Hanselmann
  @param test: a function taking one argument that should return True
432 3865ca48 Michael Hanselmann
      if the a given object is valid; the default value is
433 3865ca48 Michael Hanselmann
      os.path.exists, causing only existing files to be returned
434 3865ca48 Michael Hanselmann
  @rtype: str or None
435 3865ca48 Michael Hanselmann
  @return: full path to the object if found, None otherwise
436 3865ca48 Michael Hanselmann

437 3865ca48 Michael Hanselmann
  """
438 3865ca48 Michael Hanselmann
  # validate the filename mask
439 3865ca48 Michael Hanselmann
  if constants.EXT_PLUGIN_MASK.match(name) is None:
440 3865ca48 Michael Hanselmann
    logging.critical("Invalid value passed for external script name: '%s'",
441 3865ca48 Michael Hanselmann
                     name)
442 3865ca48 Michael Hanselmann
    return None
443 3865ca48 Michael Hanselmann
444 3865ca48 Michael Hanselmann
  for dir_name in search_path:
445 3865ca48 Michael Hanselmann
    # FIXME: investigate switch to PathJoin
446 3865ca48 Michael Hanselmann
    item_name = os.path.sep.join([dir_name, name])
447 3865ca48 Michael Hanselmann
    # check the user test and that we're indeed resolving to the given
448 3865ca48 Michael Hanselmann
    # basename
449 3865ca48 Michael Hanselmann
    if test(item_name) and os.path.basename(item_name) == name:
450 3865ca48 Michael Hanselmann
      return item_name
451 3865ca48 Michael Hanselmann
  return None
452 3865ca48 Michael Hanselmann
453 3865ca48 Michael Hanselmann
454 3865ca48 Michael Hanselmann
def IsNormAbsPath(path):
455 3865ca48 Michael Hanselmann
  """Check whether a path is absolute and also normalized
456 3865ca48 Michael Hanselmann

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

459 3865ca48 Michael Hanselmann
  """
460 3865ca48 Michael Hanselmann
  return os.path.normpath(path) == path and os.path.isabs(path)
461 3865ca48 Michael Hanselmann
462 3865ca48 Michael Hanselmann
463 3865ca48 Michael Hanselmann
def PathJoin(*args):
464 3865ca48 Michael Hanselmann
  """Safe-join a list of path components.
465 3865ca48 Michael Hanselmann

466 3865ca48 Michael Hanselmann
  Requirements:
467 3865ca48 Michael Hanselmann
      - the first argument must be an absolute path
468 3865ca48 Michael Hanselmann
      - no component in the path must have backtracking (e.g. /../),
469 3865ca48 Michael Hanselmann
        since we check for normalization at the end
470 3865ca48 Michael Hanselmann

471 3865ca48 Michael Hanselmann
  @param args: the path components to be joined
472 3865ca48 Michael Hanselmann
  @raise ValueError: for invalid paths
473 3865ca48 Michael Hanselmann

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

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

500 3865ca48 Michael Hanselmann
  @param fname: the file name
501 3865ca48 Michael Hanselmann
  @type lines: int
502 3865ca48 Michael Hanselmann
  @param lines: the (maximum) number of lines to return
503 3865ca48 Michael Hanselmann

504 3865ca48 Michael Hanselmann
  """
505 3865ca48 Michael Hanselmann
  fd = open(fname, "r")
506 3865ca48 Michael Hanselmann
  try:
507 3865ca48 Michael Hanselmann
    fd.seek(0, 2)
508 3865ca48 Michael Hanselmann
    pos = fd.tell()
509 3865ca48 Michael Hanselmann
    pos = max(0, pos-4096)
510 3865ca48 Michael Hanselmann
    fd.seek(pos, 0)
511 3865ca48 Michael Hanselmann
    raw_data = fd.read()
512 3865ca48 Michael Hanselmann
  finally:
513 3865ca48 Michael Hanselmann
    fd.close()
514 3865ca48 Michael Hanselmann
515 3865ca48 Michael Hanselmann
  rows = raw_data.splitlines()
516 3865ca48 Michael Hanselmann
  return rows[-lines:]
517 3865ca48 Michael Hanselmann
518 3865ca48 Michael Hanselmann
519 3865ca48 Michael Hanselmann
def BytesToMebibyte(value):
520 3865ca48 Michael Hanselmann
  """Converts bytes to mebibytes.
521 3865ca48 Michael Hanselmann

522 3865ca48 Michael Hanselmann
  @type value: int
523 3865ca48 Michael Hanselmann
  @param value: Value in bytes
524 3865ca48 Michael Hanselmann
  @rtype: int
525 3865ca48 Michael Hanselmann
  @return: Value in mebibytes
526 3865ca48 Michael Hanselmann

527 3865ca48 Michael Hanselmann
  """
528 3865ca48 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
529 3865ca48 Michael Hanselmann
530 3865ca48 Michael Hanselmann
531 3865ca48 Michael Hanselmann
def CalculateDirectorySize(path):
532 3865ca48 Michael Hanselmann
  """Calculates the size of a directory recursively.
533 3865ca48 Michael Hanselmann

534 3865ca48 Michael Hanselmann
  @type path: string
535 3865ca48 Michael Hanselmann
  @param path: Path to directory
536 3865ca48 Michael Hanselmann
  @rtype: int
537 3865ca48 Michael Hanselmann
  @return: Size in mebibytes
538 3865ca48 Michael Hanselmann

539 3865ca48 Michael Hanselmann
  """
540 3865ca48 Michael Hanselmann
  size = 0
541 3865ca48 Michael Hanselmann
542 3865ca48 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
543 3865ca48 Michael Hanselmann
    for filename in files:
544 3865ca48 Michael Hanselmann
      st = os.lstat(PathJoin(curpath, filename))
545 3865ca48 Michael Hanselmann
      size += st.st_size
546 3865ca48 Michael Hanselmann
547 3865ca48 Michael Hanselmann
  return BytesToMebibyte(size)
548 3865ca48 Michael Hanselmann
549 3865ca48 Michael Hanselmann
550 3865ca48 Michael Hanselmann
def GetFilesystemStats(path):
551 3865ca48 Michael Hanselmann
  """Returns the total and free space on a filesystem.
552 3865ca48 Michael Hanselmann

553 3865ca48 Michael Hanselmann
  @type path: string
554 3865ca48 Michael Hanselmann
  @param path: Path on filesystem to be examined
555 3865ca48 Michael Hanselmann
  @rtype: int
556 3865ca48 Michael Hanselmann
  @return: tuple of (Total space, Free space) in mebibytes
557 3865ca48 Michael Hanselmann

558 3865ca48 Michael Hanselmann
  """
559 3865ca48 Michael Hanselmann
  st = os.statvfs(path)
560 3865ca48 Michael Hanselmann
561 3865ca48 Michael Hanselmann
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
562 3865ca48 Michael Hanselmann
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
563 3865ca48 Michael Hanselmann
  return (tsize, fsize)
564 3865ca48 Michael Hanselmann
565 3865ca48 Michael Hanselmann
566 3865ca48 Michael Hanselmann
def ReadPidFile(pidfile):
567 3865ca48 Michael Hanselmann
  """Read a pid from a file.
568 3865ca48 Michael Hanselmann

569 3865ca48 Michael Hanselmann
  @type  pidfile: string
570 3865ca48 Michael Hanselmann
  @param pidfile: path to the file containing the pid
571 3865ca48 Michael Hanselmann
  @rtype: int
572 3865ca48 Michael Hanselmann
  @return: The process id, if the file exists and contains a valid PID,
573 3865ca48 Michael Hanselmann
           otherwise 0
574 3865ca48 Michael Hanselmann

575 3865ca48 Michael Hanselmann
  """
576 3865ca48 Michael Hanselmann
  try:
577 3865ca48 Michael Hanselmann
    raw_data = ReadOneLineFile(pidfile)
578 3865ca48 Michael Hanselmann
  except EnvironmentError, err:
579 3865ca48 Michael Hanselmann
    if err.errno != errno.ENOENT:
580 3865ca48 Michael Hanselmann
      logging.exception("Can't read pid file")
581 3865ca48 Michael Hanselmann
    return 0
582 3865ca48 Michael Hanselmann
583 3865ca48 Michael Hanselmann
  try:
584 3865ca48 Michael Hanselmann
    pid = int(raw_data)
585 3865ca48 Michael Hanselmann
  except (TypeError, ValueError), err:
586 3865ca48 Michael Hanselmann
    logging.info("Can't parse pid file contents", exc_info=True)
587 3865ca48 Michael Hanselmann
    return 0
588 3865ca48 Michael Hanselmann
589 3865ca48 Michael Hanselmann
  return pid
590 3865ca48 Michael Hanselmann
591 3865ca48 Michael Hanselmann
592 3865ca48 Michael Hanselmann
def ReadLockedPidFile(path):
593 3865ca48 Michael Hanselmann
  """Reads a locked PID file.
594 3865ca48 Michael Hanselmann

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

597 3865ca48 Michael Hanselmann
  @type path: string
598 3865ca48 Michael Hanselmann
  @param path: Path to PID file
599 3865ca48 Michael Hanselmann
  @return: PID as integer or, if file was unlocked or couldn't be opened, None
600 3865ca48 Michael Hanselmann

601 3865ca48 Michael Hanselmann
  """
602 3865ca48 Michael Hanselmann
  try:
603 3865ca48 Michael Hanselmann
    fd = os.open(path, os.O_RDONLY)
604 3865ca48 Michael Hanselmann
  except EnvironmentError, err:
605 3865ca48 Michael Hanselmann
    if err.errno == errno.ENOENT:
606 3865ca48 Michael Hanselmann
      # PID file doesn't exist
607 3865ca48 Michael Hanselmann
      return None
608 3865ca48 Michael Hanselmann
    raise
609 3865ca48 Michael Hanselmann
610 3865ca48 Michael Hanselmann
  try:
611 3865ca48 Michael Hanselmann
    try:
612 3865ca48 Michael Hanselmann
      # Try to acquire lock
613 3865ca48 Michael Hanselmann
      filelock.LockFile(fd)
614 3865ca48 Michael Hanselmann
    except errors.LockError:
615 3865ca48 Michael Hanselmann
      # Couldn't lock, daemon is running
616 3865ca48 Michael Hanselmann
      return int(os.read(fd, 100))
617 3865ca48 Michael Hanselmann
  finally:
618 3865ca48 Michael Hanselmann
    os.close(fd)
619 3865ca48 Michael Hanselmann
620 3865ca48 Michael Hanselmann
  return None
621 3865ca48 Michael Hanselmann
622 3865ca48 Michael Hanselmann
623 3865ca48 Michael Hanselmann
def AddAuthorizedKey(file_obj, key):
624 3865ca48 Michael Hanselmann
  """Adds an SSH public key to an authorized_keys file.
625 3865ca48 Michael Hanselmann

626 3865ca48 Michael Hanselmann
  @type file_obj: str or file handle
627 3865ca48 Michael Hanselmann
  @param file_obj: path to authorized_keys file
628 3865ca48 Michael Hanselmann
  @type key: str
629 3865ca48 Michael Hanselmann
  @param key: string containing key
630 3865ca48 Michael Hanselmann

631 3865ca48 Michael Hanselmann
  """
632 3865ca48 Michael Hanselmann
  key_fields = key.split()
633 3865ca48 Michael Hanselmann
634 3865ca48 Michael Hanselmann
  if isinstance(file_obj, basestring):
635 3865ca48 Michael Hanselmann
    f = open(file_obj, 'a+')
636 3865ca48 Michael Hanselmann
  else:
637 3865ca48 Michael Hanselmann
    f = file_obj
638 3865ca48 Michael Hanselmann
639 3865ca48 Michael Hanselmann
  try:
640 3865ca48 Michael Hanselmann
    nl = True
641 3865ca48 Michael Hanselmann
    for line in f:
642 3865ca48 Michael Hanselmann
      # Ignore whitespace changes
643 3865ca48 Michael Hanselmann
      if line.split() == key_fields:
644 3865ca48 Michael Hanselmann
        break
645 3865ca48 Michael Hanselmann
      nl = line.endswith('\n')
646 3865ca48 Michael Hanselmann
    else:
647 3865ca48 Michael Hanselmann
      if not nl:
648 3865ca48 Michael Hanselmann
        f.write("\n")
649 3865ca48 Michael Hanselmann
      f.write(key.rstrip('\r\n'))
650 3865ca48 Michael Hanselmann
      f.write("\n")
651 3865ca48 Michael Hanselmann
      f.flush()
652 3865ca48 Michael Hanselmann
  finally:
653 3865ca48 Michael Hanselmann
    f.close()
654 3865ca48 Michael Hanselmann
655 3865ca48 Michael Hanselmann
656 3865ca48 Michael Hanselmann
def RemoveAuthorizedKey(file_name, key):
657 3865ca48 Michael Hanselmann
  """Removes an SSH public key from an authorized_keys file.
658 3865ca48 Michael Hanselmann

659 3865ca48 Michael Hanselmann
  @type file_name: str
660 3865ca48 Michael Hanselmann
  @param file_name: path to authorized_keys file
661 3865ca48 Michael Hanselmann
  @type key: str
662 3865ca48 Michael Hanselmann
  @param key: string containing key
663 3865ca48 Michael Hanselmann

664 3865ca48 Michael Hanselmann
  """
665 3865ca48 Michael Hanselmann
  key_fields = key.split()
666 3865ca48 Michael Hanselmann
667 3865ca48 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
668 3865ca48 Michael Hanselmann
  try:
669 3865ca48 Michael Hanselmann
    out = os.fdopen(fd, 'w')
670 3865ca48 Michael Hanselmann
    try:
671 3865ca48 Michael Hanselmann
      f = open(file_name, 'r')
672 3865ca48 Michael Hanselmann
      try:
673 3865ca48 Michael Hanselmann
        for line in f:
674 3865ca48 Michael Hanselmann
          # Ignore whitespace changes while comparing lines
675 3865ca48 Michael Hanselmann
          if line.split() != key_fields:
676 3865ca48 Michael Hanselmann
            out.write(line)
677 3865ca48 Michael Hanselmann
678 3865ca48 Michael Hanselmann
        out.flush()
679 3865ca48 Michael Hanselmann
        os.rename(tmpname, file_name)
680 3865ca48 Michael Hanselmann
      finally:
681 3865ca48 Michael Hanselmann
        f.close()
682 3865ca48 Michael Hanselmann
    finally:
683 3865ca48 Michael Hanselmann
      out.close()
684 3865ca48 Michael Hanselmann
  except:
685 3865ca48 Michael Hanselmann
    RemoveFile(tmpname)
686 3865ca48 Michael Hanselmann
    raise
687 3865ca48 Michael Hanselmann
688 3865ca48 Michael Hanselmann
689 3865ca48 Michael Hanselmann
def DaemonPidFileName(name):
690 3865ca48 Michael Hanselmann
  """Compute a ganeti pid file absolute path
691 3865ca48 Michael Hanselmann

692 3865ca48 Michael Hanselmann
  @type name: str
693 3865ca48 Michael Hanselmann
  @param name: the daemon name
694 3865ca48 Michael Hanselmann
  @rtype: str
695 3865ca48 Michael Hanselmann
  @return: the full path to the pidfile corresponding to the given
696 3865ca48 Michael Hanselmann
      daemon name
697 3865ca48 Michael Hanselmann

698 3865ca48 Michael Hanselmann
  """
699 3865ca48 Michael Hanselmann
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
700 3865ca48 Michael Hanselmann
701 3865ca48 Michael Hanselmann
702 3865ca48 Michael Hanselmann
def WritePidFile(pidfile):
703 3865ca48 Michael Hanselmann
  """Write the current process pidfile.
704 3865ca48 Michael Hanselmann

705 3865ca48 Michael Hanselmann
  @type pidfile: string
706 3865ca48 Michael Hanselmann
  @param pidfile: the path to the file to be written
707 3865ca48 Michael Hanselmann
  @raise errors.LockError: if the pid file already exists and
708 3865ca48 Michael Hanselmann
      points to a live process
709 3865ca48 Michael Hanselmann
  @rtype: int
710 3865ca48 Michael Hanselmann
  @return: the file descriptor of the lock file; do not close this unless
711 3865ca48 Michael Hanselmann
      you want to unlock the pid file
712 3865ca48 Michael Hanselmann

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

732 3865ca48 Michael Hanselmann
  @type filename: string
733 3865ca48 Michael Hanselmann
  @param filename: Path to watcher pause file
734 3865ca48 Michael Hanselmann
  @type now: None, float or int
735 3865ca48 Michael Hanselmann
  @param now: Current time as Unix timestamp
736 3865ca48 Michael Hanselmann
  @type remove_after: int
737 3865ca48 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
738 3865ca48 Michael Hanselmann
    seconds past the pause end time
739 3865ca48 Michael Hanselmann

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

775 90e234a6 Michael Hanselmann
  @note: This is a Linux-specific method as it uses the /proc
776 90e234a6 Michael Hanselmann
      filesystem.
777 90e234a6 Michael Hanselmann
  @rtype: str
778 90e234a6 Michael Hanselmann

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