Statistics
| Branch: | Tag: | Revision:

root / lib / utils / io.py @ 8e5a705d

History | View | Annotate | Download (20.8 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 8e5a705d René Nussbaumer
def RenameFile(old, new, mkdir=False, mkdir_mode=0750, dir_uid=None,
294 8e5a705d René Nussbaumer
               dir_gid=None):
295 3865ca48 Michael Hanselmann
  """Renames a file.
296 3865ca48 Michael Hanselmann

297 3865ca48 Michael Hanselmann
  @type old: string
298 3865ca48 Michael Hanselmann
  @param old: Original path
299 3865ca48 Michael Hanselmann
  @type new: string
300 3865ca48 Michael Hanselmann
  @param new: New path
301 3865ca48 Michael Hanselmann
  @type mkdir: bool
302 3865ca48 Michael Hanselmann
  @param mkdir: Whether to create target directory if it doesn't exist
303 3865ca48 Michael Hanselmann
  @type mkdir_mode: int
304 3865ca48 Michael Hanselmann
  @param mkdir_mode: Mode for newly created directories
305 8e5a705d René Nussbaumer
  @type dir_uid: int
306 8e5a705d René Nussbaumer
  @param dir_uid: The uid for the (if fresh created) dir
307 8e5a705d René Nussbaumer
  @type dir_gid: int
308 8e5a705d René Nussbaumer
  @param dir_gid: The gid for the (if fresh created) dir
309 3865ca48 Michael Hanselmann

310 3865ca48 Michael Hanselmann
  """
311 3865ca48 Michael Hanselmann
  try:
312 3865ca48 Michael Hanselmann
    return os.rename(old, new)
313 3865ca48 Michael Hanselmann
  except OSError, err:
314 3865ca48 Michael Hanselmann
    # In at least one use case of this function, the job queue, directory
315 3865ca48 Michael Hanselmann
    # creation is very rare. Checking for the directory before renaming is not
316 3865ca48 Michael Hanselmann
    # as efficient.
317 3865ca48 Michael Hanselmann
    if mkdir and err.errno == errno.ENOENT:
318 3865ca48 Michael Hanselmann
      # Create directory and try again
319 8e5a705d René Nussbaumer
      dir_path = os.path.dirname(new)
320 8e5a705d René Nussbaumer
      Makedirs(dir_path, mode=mkdir_mode)
321 8e5a705d René Nussbaumer
      if not (dir_uid is None or dir_gid is None):
322 8e5a705d René Nussbaumer
        os.chown(dir_path, dir_uid, dir_gid)
323 3865ca48 Michael Hanselmann
324 3865ca48 Michael Hanselmann
      return os.rename(old, new)
325 3865ca48 Michael Hanselmann
326 3865ca48 Michael Hanselmann
    raise
327 3865ca48 Michael Hanselmann
328 3865ca48 Michael Hanselmann
329 3865ca48 Michael Hanselmann
def Makedirs(path, mode=0750):
330 3865ca48 Michael Hanselmann
  """Super-mkdir; create a leaf directory and all intermediate ones.
331 3865ca48 Michael Hanselmann

332 3865ca48 Michael Hanselmann
  This is a wrapper around C{os.makedirs} adding error handling not implemented
333 3865ca48 Michael Hanselmann
  before Python 2.5.
334 3865ca48 Michael Hanselmann

335 3865ca48 Michael Hanselmann
  """
336 3865ca48 Michael Hanselmann
  try:
337 3865ca48 Michael Hanselmann
    os.makedirs(path, mode)
338 3865ca48 Michael Hanselmann
  except OSError, err:
339 3865ca48 Michael Hanselmann
    # Ignore EEXIST. This is only handled in os.makedirs as included in
340 3865ca48 Michael Hanselmann
    # Python 2.5 and above.
341 3865ca48 Michael Hanselmann
    if err.errno != errno.EEXIST or not os.path.exists(path):
342 3865ca48 Michael Hanselmann
      raise
343 3865ca48 Michael Hanselmann
344 3865ca48 Michael Hanselmann
345 3865ca48 Michael Hanselmann
def TimestampForFilename():
346 3865ca48 Michael Hanselmann
  """Returns the current time formatted for filenames.
347 3865ca48 Michael Hanselmann

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

351 3865ca48 Michael Hanselmann
  """
352 3865ca48 Michael Hanselmann
  return time.strftime("%Y-%m-%d_%H_%M_%S")
353 3865ca48 Michael Hanselmann
354 3865ca48 Michael Hanselmann
355 3865ca48 Michael Hanselmann
def CreateBackup(file_name):
356 3865ca48 Michael Hanselmann
  """Creates a backup of a file.
357 3865ca48 Michael Hanselmann

358 3865ca48 Michael Hanselmann
  @type file_name: str
359 3865ca48 Michael Hanselmann
  @param file_name: file to be backed up
360 3865ca48 Michael Hanselmann
  @rtype: str
361 3865ca48 Michael Hanselmann
  @return: the path to the newly created backup
362 3865ca48 Michael Hanselmann
  @raise errors.ProgrammerError: for invalid file names
363 3865ca48 Michael Hanselmann

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

391 3865ca48 Michael Hanselmann
  @type path: str
392 3865ca48 Michael Hanselmann
  @param path: the directory to enumerate
393 3865ca48 Michael Hanselmann
  @rtype: list
394 3865ca48 Michael Hanselmann
  @return: the list of all files not starting with a dot
395 3865ca48 Michael Hanselmann
  @raise ProgrammerError: if L{path} is not an absolue and normalized path
396 3865ca48 Michael Hanselmann

397 3865ca48 Michael Hanselmann
  """
398 3865ca48 Michael Hanselmann
  if not IsNormAbsPath(path):
399 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("Path passed to ListVisibleFiles is not"
400 3865ca48 Michael Hanselmann
                                 " absolute/normalized: '%s'" % path)
401 3865ca48 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
402 3865ca48 Michael Hanselmann
  return files
403 3865ca48 Michael Hanselmann
404 3865ca48 Michael Hanselmann
405 3865ca48 Michael Hanselmann
def EnsureDirs(dirs):
406 3865ca48 Michael Hanselmann
  """Make required directories, if they don't exist.
407 3865ca48 Michael Hanselmann

408 3865ca48 Michael Hanselmann
  @param dirs: list of tuples (dir_name, dir_mode)
409 3865ca48 Michael Hanselmann
  @type dirs: list of (string, integer)
410 3865ca48 Michael Hanselmann

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

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

434 3865ca48 Michael Hanselmann
  @type name: str
435 3865ca48 Michael Hanselmann
  @param name: the name to look for
436 3865ca48 Michael Hanselmann
  @type search_path: str
437 3865ca48 Michael Hanselmann
  @param search_path: location to start at
438 3865ca48 Michael Hanselmann
  @type test: callable
439 3865ca48 Michael Hanselmann
  @param test: a function taking one argument that should return True
440 3865ca48 Michael Hanselmann
      if the a given object is valid; the default value is
441 3865ca48 Michael Hanselmann
      os.path.exists, causing only existing files to be returned
442 3865ca48 Michael Hanselmann
  @rtype: str or None
443 3865ca48 Michael Hanselmann
  @return: full path to the object if found, None otherwise
444 3865ca48 Michael Hanselmann

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

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

467 3865ca48 Michael Hanselmann
  """
468 3865ca48 Michael Hanselmann
  return os.path.normpath(path) == path and os.path.isabs(path)
469 3865ca48 Michael Hanselmann
470 3865ca48 Michael Hanselmann
471 3865ca48 Michael Hanselmann
def PathJoin(*args):
472 3865ca48 Michael Hanselmann
  """Safe-join a list of path components.
473 3865ca48 Michael Hanselmann

474 3865ca48 Michael Hanselmann
  Requirements:
475 3865ca48 Michael Hanselmann
      - the first argument must be an absolute path
476 3865ca48 Michael Hanselmann
      - no component in the path must have backtracking (e.g. /../),
477 3865ca48 Michael Hanselmann
        since we check for normalization at the end
478 3865ca48 Michael Hanselmann

479 3865ca48 Michael Hanselmann
  @param args: the path components to be joined
480 3865ca48 Michael Hanselmann
  @raise ValueError: for invalid paths
481 3865ca48 Michael Hanselmann

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

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

508 3865ca48 Michael Hanselmann
  @param fname: the file name
509 3865ca48 Michael Hanselmann
  @type lines: int
510 3865ca48 Michael Hanselmann
  @param lines: the (maximum) number of lines to return
511 3865ca48 Michael Hanselmann

512 3865ca48 Michael Hanselmann
  """
513 3865ca48 Michael Hanselmann
  fd = open(fname, "r")
514 3865ca48 Michael Hanselmann
  try:
515 3865ca48 Michael Hanselmann
    fd.seek(0, 2)
516 3865ca48 Michael Hanselmann
    pos = fd.tell()
517 3865ca48 Michael Hanselmann
    pos = max(0, pos-4096)
518 3865ca48 Michael Hanselmann
    fd.seek(pos, 0)
519 3865ca48 Michael Hanselmann
    raw_data = fd.read()
520 3865ca48 Michael Hanselmann
  finally:
521 3865ca48 Michael Hanselmann
    fd.close()
522 3865ca48 Michael Hanselmann
523 3865ca48 Michael Hanselmann
  rows = raw_data.splitlines()
524 3865ca48 Michael Hanselmann
  return rows[-lines:]
525 3865ca48 Michael Hanselmann
526 3865ca48 Michael Hanselmann
527 3865ca48 Michael Hanselmann
def BytesToMebibyte(value):
528 3865ca48 Michael Hanselmann
  """Converts bytes to mebibytes.
529 3865ca48 Michael Hanselmann

530 3865ca48 Michael Hanselmann
  @type value: int
531 3865ca48 Michael Hanselmann
  @param value: Value in bytes
532 3865ca48 Michael Hanselmann
  @rtype: int
533 3865ca48 Michael Hanselmann
  @return: Value in mebibytes
534 3865ca48 Michael Hanselmann

535 3865ca48 Michael Hanselmann
  """
536 3865ca48 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
537 3865ca48 Michael Hanselmann
538 3865ca48 Michael Hanselmann
539 3865ca48 Michael Hanselmann
def CalculateDirectorySize(path):
540 3865ca48 Michael Hanselmann
  """Calculates the size of a directory recursively.
541 3865ca48 Michael Hanselmann

542 3865ca48 Michael Hanselmann
  @type path: string
543 3865ca48 Michael Hanselmann
  @param path: Path to directory
544 3865ca48 Michael Hanselmann
  @rtype: int
545 3865ca48 Michael Hanselmann
  @return: Size in mebibytes
546 3865ca48 Michael Hanselmann

547 3865ca48 Michael Hanselmann
  """
548 3865ca48 Michael Hanselmann
  size = 0
549 3865ca48 Michael Hanselmann
550 3865ca48 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
551 3865ca48 Michael Hanselmann
    for filename in files:
552 3865ca48 Michael Hanselmann
      st = os.lstat(PathJoin(curpath, filename))
553 3865ca48 Michael Hanselmann
      size += st.st_size
554 3865ca48 Michael Hanselmann
555 3865ca48 Michael Hanselmann
  return BytesToMebibyte(size)
556 3865ca48 Michael Hanselmann
557 3865ca48 Michael Hanselmann
558 3865ca48 Michael Hanselmann
def GetFilesystemStats(path):
559 3865ca48 Michael Hanselmann
  """Returns the total and free space on a filesystem.
560 3865ca48 Michael Hanselmann

561 3865ca48 Michael Hanselmann
  @type path: string
562 3865ca48 Michael Hanselmann
  @param path: Path on filesystem to be examined
563 3865ca48 Michael Hanselmann
  @rtype: int
564 3865ca48 Michael Hanselmann
  @return: tuple of (Total space, Free space) in mebibytes
565 3865ca48 Michael Hanselmann

566 3865ca48 Michael Hanselmann
  """
567 3865ca48 Michael Hanselmann
  st = os.statvfs(path)
568 3865ca48 Michael Hanselmann
569 3865ca48 Michael Hanselmann
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
570 3865ca48 Michael Hanselmann
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
571 3865ca48 Michael Hanselmann
  return (tsize, fsize)
572 3865ca48 Michael Hanselmann
573 3865ca48 Michael Hanselmann
574 3865ca48 Michael Hanselmann
def ReadPidFile(pidfile):
575 3865ca48 Michael Hanselmann
  """Read a pid from a file.
576 3865ca48 Michael Hanselmann

577 3865ca48 Michael Hanselmann
  @type  pidfile: string
578 3865ca48 Michael Hanselmann
  @param pidfile: path to the file containing the pid
579 3865ca48 Michael Hanselmann
  @rtype: int
580 3865ca48 Michael Hanselmann
  @return: The process id, if the file exists and contains a valid PID,
581 3865ca48 Michael Hanselmann
           otherwise 0
582 3865ca48 Michael Hanselmann

583 3865ca48 Michael Hanselmann
  """
584 3865ca48 Michael Hanselmann
  try:
585 3865ca48 Michael Hanselmann
    raw_data = ReadOneLineFile(pidfile)
586 3865ca48 Michael Hanselmann
  except EnvironmentError, err:
587 3865ca48 Michael Hanselmann
    if err.errno != errno.ENOENT:
588 3865ca48 Michael Hanselmann
      logging.exception("Can't read pid file")
589 3865ca48 Michael Hanselmann
    return 0
590 3865ca48 Michael Hanselmann
591 3865ca48 Michael Hanselmann
  try:
592 3865ca48 Michael Hanselmann
    pid = int(raw_data)
593 3865ca48 Michael Hanselmann
  except (TypeError, ValueError), err:
594 3865ca48 Michael Hanselmann
    logging.info("Can't parse pid file contents", exc_info=True)
595 3865ca48 Michael Hanselmann
    return 0
596 3865ca48 Michael Hanselmann
597 3865ca48 Michael Hanselmann
  return pid
598 3865ca48 Michael Hanselmann
599 3865ca48 Michael Hanselmann
600 3865ca48 Michael Hanselmann
def ReadLockedPidFile(path):
601 3865ca48 Michael Hanselmann
  """Reads a locked PID file.
602 3865ca48 Michael Hanselmann

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

605 3865ca48 Michael Hanselmann
  @type path: string
606 3865ca48 Michael Hanselmann
  @param path: Path to PID file
607 3865ca48 Michael Hanselmann
  @return: PID as integer or, if file was unlocked or couldn't be opened, None
608 3865ca48 Michael Hanselmann

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

634 3865ca48 Michael Hanselmann
  @type file_obj: str or file handle
635 3865ca48 Michael Hanselmann
  @param file_obj: path to authorized_keys file
636 3865ca48 Michael Hanselmann
  @type key: str
637 3865ca48 Michael Hanselmann
  @param key: string containing key
638 3865ca48 Michael Hanselmann

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

667 3865ca48 Michael Hanselmann
  @type file_name: str
668 3865ca48 Michael Hanselmann
  @param file_name: path to authorized_keys file
669 3865ca48 Michael Hanselmann
  @type key: str
670 3865ca48 Michael Hanselmann
  @param key: string containing key
671 3865ca48 Michael Hanselmann

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

700 3865ca48 Michael Hanselmann
  @type name: str
701 3865ca48 Michael Hanselmann
  @param name: the daemon name
702 3865ca48 Michael Hanselmann
  @rtype: str
703 3865ca48 Michael Hanselmann
  @return: the full path to the pidfile corresponding to the given
704 3865ca48 Michael Hanselmann
      daemon name
705 3865ca48 Michael Hanselmann

706 3865ca48 Michael Hanselmann
  """
707 3865ca48 Michael Hanselmann
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
708 3865ca48 Michael Hanselmann
709 3865ca48 Michael Hanselmann
710 3865ca48 Michael Hanselmann
def WritePidFile(pidfile):
711 3865ca48 Michael Hanselmann
  """Write the current process pidfile.
712 3865ca48 Michael Hanselmann

713 3865ca48 Michael Hanselmann
  @type pidfile: string
714 3865ca48 Michael Hanselmann
  @param pidfile: the path to the file to be written
715 3865ca48 Michael Hanselmann
  @raise errors.LockError: if the pid file already exists and
716 3865ca48 Michael Hanselmann
      points to a live process
717 3865ca48 Michael Hanselmann
  @rtype: int
718 3865ca48 Michael Hanselmann
  @return: the file descriptor of the lock file; do not close this unless
719 3865ca48 Michael Hanselmann
      you want to unlock the pid file
720 3865ca48 Michael Hanselmann

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

740 3865ca48 Michael Hanselmann
  @type filename: string
741 3865ca48 Michael Hanselmann
  @param filename: Path to watcher pause file
742 3865ca48 Michael Hanselmann
  @type now: None, float or int
743 3865ca48 Michael Hanselmann
  @param now: Current time as Unix timestamp
744 3865ca48 Michael Hanselmann
  @type remove_after: int
745 3865ca48 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
746 3865ca48 Michael Hanselmann
    seconds past the pause end time
747 3865ca48 Michael Hanselmann

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

783 90e234a6 Michael Hanselmann
  @note: This is a Linux-specific method as it uses the /proc
784 90e234a6 Michael Hanselmann
      filesystem.
785 90e234a6 Michael Hanselmann
  @rtype: str
786 90e234a6 Michael Hanselmann

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