Statistics
| Branch: | Tag: | Revision:

root / lib / utils / io.py @ 0aee8ee9

History | View | Annotate | Download (21.2 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 0e5084ee Michael Hanselmann
def ReadFile(file_name, size=-1, preread=None):
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 0e5084ee Michael Hanselmann
  @type preread: callable receiving file handle as single parameter
47 0e5084ee Michael Hanselmann
  @param preread: Function called before file is read
48 3865ca48 Michael Hanselmann
  @rtype: str
49 3865ca48 Michael Hanselmann
  @return: the (possibly partial) content of the file
50 3865ca48 Michael Hanselmann

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

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

75 3865ca48 Michael Hanselmann
  If the function doesn't raise an exception, it has succeeded and the
76 3865ca48 Michael Hanselmann
  target file has the new contents. If the function has raised an
77 3865ca48 Michael Hanselmann
  exception, an existing target file should be unmodified and the
78 3865ca48 Michael Hanselmann
  temporary file should be removed.
79 3865ca48 Michael Hanselmann

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

104 3865ca48 Michael Hanselmann
  @rtype: None or int
105 3865ca48 Michael Hanselmann
  @return: None if the 'close' parameter evaluates to True,
106 3865ca48 Michael Hanselmann
      otherwise the file descriptor
107 3865ca48 Michael Hanselmann

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

110 3865ca48 Michael Hanselmann
  """
111 3865ca48 Michael Hanselmann
  if not os.path.isabs(file_name):
112 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("Path passed to WriteFile is not"
113 3865ca48 Michael Hanselmann
                                 " absolute: '%s'" % file_name)
114 3865ca48 Michael Hanselmann
115 3865ca48 Michael Hanselmann
  if [fn, data].count(None) != 1:
116 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("fn or data required")
117 3865ca48 Michael Hanselmann
118 3865ca48 Michael Hanselmann
  if [atime, mtime].count(None) == 1:
119 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("Both atime and mtime must be either"
120 3865ca48 Michael Hanselmann
                                 " set or None")
121 3865ca48 Michael Hanselmann
122 3865ca48 Michael Hanselmann
  if backup and not dry_run and os.path.isfile(file_name):
123 3865ca48 Michael Hanselmann
    CreateBackup(file_name)
124 3865ca48 Michael Hanselmann
125 a9d68e40 Michael Hanselmann
  # Whether temporary file needs to be removed (e.g. if any error occurs)
126 3865ca48 Michael Hanselmann
  do_remove = True
127 a9d68e40 Michael Hanselmann
128 a9d68e40 Michael Hanselmann
  # Function result
129 a9d68e40 Michael Hanselmann
  result = None
130 a9d68e40 Michael Hanselmann
131 a9d68e40 Michael Hanselmann
  (dir_name, base_name) = os.path.split(file_name)
132 a9d68e40 Michael Hanselmann
  (fd, new_name) = tempfile.mkstemp(suffix=".new", prefix=base_name,
133 a9d68e40 Michael Hanselmann
                                    dir=dir_name)
134 3865ca48 Michael Hanselmann
  try:
135 a9d68e40 Michael Hanselmann
    try:
136 a9d68e40 Michael Hanselmann
      if uid != -1 or gid != -1:
137 a9d68e40 Michael Hanselmann
        os.chown(new_name, uid, gid)
138 a9d68e40 Michael Hanselmann
      if mode:
139 a9d68e40 Michael Hanselmann
        os.chmod(new_name, mode)
140 a9d68e40 Michael Hanselmann
      if callable(prewrite):
141 a9d68e40 Michael Hanselmann
        prewrite(fd)
142 a9d68e40 Michael Hanselmann
      if data is not None:
143 1d39e245 Iustin Pop
        if isinstance(data, unicode):
144 1d39e245 Iustin Pop
          data = data.encode()
145 1d39e245 Iustin Pop
        assert isinstance(data, str)
146 437c3e77 Iustin Pop
        to_write = len(data)
147 437c3e77 Iustin Pop
        offset = 0
148 437c3e77 Iustin Pop
        while offset < to_write:
149 437c3e77 Iustin Pop
          written = os.write(fd, buffer(data, offset))
150 437c3e77 Iustin Pop
          assert written >= 0
151 1d39e245 Iustin Pop
          assert written <= to_write - offset
152 437c3e77 Iustin Pop
          offset += written
153 437c3e77 Iustin Pop
        assert offset == to_write
154 a9d68e40 Michael Hanselmann
      else:
155 a9d68e40 Michael Hanselmann
        fn(fd)
156 a9d68e40 Michael Hanselmann
      if callable(postwrite):
157 a9d68e40 Michael Hanselmann
        postwrite(fd)
158 a9d68e40 Michael Hanselmann
      os.fsync(fd)
159 a9d68e40 Michael Hanselmann
      if atime is not None and mtime is not None:
160 a9d68e40 Michael Hanselmann
        os.utime(new_name, (atime, mtime))
161 a9d68e40 Michael Hanselmann
    finally:
162 a9d68e40 Michael Hanselmann
      # Close file unless the file descriptor should be returned
163 a9d68e40 Michael Hanselmann
      if close:
164 a9d68e40 Michael Hanselmann
        os.close(fd)
165 a9d68e40 Michael Hanselmann
      else:
166 a9d68e40 Michael Hanselmann
        result = fd
167 a9d68e40 Michael Hanselmann
168 a9d68e40 Michael Hanselmann
    # Rename file to destination name
169 3865ca48 Michael Hanselmann
    if not dry_run:
170 3865ca48 Michael Hanselmann
      os.rename(new_name, file_name)
171 a9d68e40 Michael Hanselmann
      # Successful, no need to remove anymore
172 3865ca48 Michael Hanselmann
      do_remove = False
173 3865ca48 Michael Hanselmann
  finally:
174 3865ca48 Michael Hanselmann
    if do_remove:
175 3865ca48 Michael Hanselmann
      RemoveFile(new_name)
176 3865ca48 Michael Hanselmann
177 3865ca48 Michael Hanselmann
  return result
178 3865ca48 Michael Hanselmann
179 3865ca48 Michael Hanselmann
180 3865ca48 Michael Hanselmann
def GetFileID(path=None, fd=None):
181 3865ca48 Michael Hanselmann
  """Returns the file 'id', i.e. the dev/inode and mtime information.
182 3865ca48 Michael Hanselmann

183 3865ca48 Michael Hanselmann
  Either the path to the file or the fd must be given.
184 3865ca48 Michael Hanselmann

185 3865ca48 Michael Hanselmann
  @param path: the file path
186 3865ca48 Michael Hanselmann
  @param fd: a file descriptor
187 3865ca48 Michael Hanselmann
  @return: a tuple of (device number, inode number, mtime)
188 3865ca48 Michael Hanselmann

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

204 3865ca48 Michael Hanselmann
  Differences in the inode/device are not accepted, but and older
205 3865ca48 Michael Hanselmann
  timestamp for fi_disk is accepted.
206 3865ca48 Michael Hanselmann

207 3865ca48 Michael Hanselmann
  @param fi_disk: tuple (dev, inode, mtime) representing the actual
208 3865ca48 Michael Hanselmann
      file data
209 3865ca48 Michael Hanselmann
  @param fi_ours: tuple (dev, inode, mtime) representing the last
210 3865ca48 Michael Hanselmann
      written file data
211 3865ca48 Michael Hanselmann
  @rtype: boolean
212 3865ca48 Michael Hanselmann

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

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

226 3865ca48 Michael Hanselmann
  @type file_name: str
227 3865ca48 Michael Hanselmann
  @param file_name: the target filename
228 3865ca48 Michael Hanselmann
  @type file_id: tuple
229 3865ca48 Michael Hanselmann
  @param file_id: a result from L{GetFileID}
230 3865ca48 Michael Hanselmann

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

248 3865ca48 Michael Hanselmann
  @type strict: boolean
249 3865ca48 Michael Hanselmann
  @param strict: if True, abort if the file has more than one
250 3865ca48 Michael Hanselmann
      non-empty line
251 3865ca48 Michael Hanselmann

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

266 3865ca48 Michael Hanselmann
  Remove a file, ignoring non-existing ones or directories. Other
267 3865ca48 Michael Hanselmann
  errors are passed.
268 3865ca48 Michael Hanselmann

269 3865ca48 Michael Hanselmann
  @type filename: str
270 3865ca48 Michael Hanselmann
  @param filename: the file to be removed
271 3865ca48 Michael Hanselmann

272 3865ca48 Michael Hanselmann
  """
273 3865ca48 Michael Hanselmann
  try:
274 3865ca48 Michael Hanselmann
    os.unlink(filename)
275 3865ca48 Michael Hanselmann
  except OSError, err:
276 3865ca48 Michael Hanselmann
    if err.errno not in (errno.ENOENT, errno.EISDIR):
277 3865ca48 Michael Hanselmann
      raise
278 3865ca48 Michael Hanselmann
279 3865ca48 Michael Hanselmann
280 3865ca48 Michael Hanselmann
def RemoveDir(dirname):
281 3865ca48 Michael Hanselmann
  """Remove an empty directory.
282 3865ca48 Michael Hanselmann

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

287 3865ca48 Michael Hanselmann
  @type dirname: str
288 3865ca48 Michael Hanselmann
  @param dirname: the empty directory to be removed
289 3865ca48 Michael Hanselmann

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

301 3865ca48 Michael Hanselmann
  @type old: string
302 3865ca48 Michael Hanselmann
  @param old: Original path
303 3865ca48 Michael Hanselmann
  @type new: string
304 3865ca48 Michael Hanselmann
  @param new: New path
305 3865ca48 Michael Hanselmann
  @type mkdir: bool
306 3865ca48 Michael Hanselmann
  @param mkdir: Whether to create target directory if it doesn't exist
307 3865ca48 Michael Hanselmann
  @type mkdir_mode: int
308 3865ca48 Michael Hanselmann
  @param mkdir_mode: Mode for newly created directories
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 3865ca48 Michael Hanselmann
      Makedirs(os.path.dirname(new), mode=mkdir_mode)
320 3865ca48 Michael Hanselmann
321 3865ca48 Michael Hanselmann
      return os.rename(old, new)
322 3865ca48 Michael Hanselmann
323 3865ca48 Michael Hanselmann
    raise
324 3865ca48 Michael Hanselmann
325 3865ca48 Michael Hanselmann
326 3865ca48 Michael Hanselmann
def Makedirs(path, mode=0750):
327 3865ca48 Michael Hanselmann
  """Super-mkdir; create a leaf directory and all intermediate ones.
328 3865ca48 Michael Hanselmann

329 3865ca48 Michael Hanselmann
  This is a wrapper around C{os.makedirs} adding error handling not implemented
330 3865ca48 Michael Hanselmann
  before Python 2.5.
331 3865ca48 Michael Hanselmann

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

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

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

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

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

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

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

405 3865ca48 Michael Hanselmann
  @param dirs: list of tuples (dir_name, dir_mode)
406 3865ca48 Michael Hanselmann
  @type dirs: list of (string, integer)
407 3865ca48 Michael Hanselmann

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

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

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

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

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

464 3865ca48 Michael Hanselmann
  """
465 3865ca48 Michael Hanselmann
  return os.path.normpath(path) == path and os.path.isabs(path)
466 3865ca48 Michael Hanselmann
467 3865ca48 Michael Hanselmann
468 fb17bebd Renรฉ Nussbaumer
def IsBelowDir(root, other_path):
469 fb17bebd Renรฉ Nussbaumer
  """Check whether a path is below a root dir.
470 fb17bebd Renรฉ Nussbaumer

471 fb17bebd Renรฉ Nussbaumer
  This works around the nasty byte-byte comparisation of commonprefix.
472 fb17bebd Renรฉ Nussbaumer

473 fb17bebd Renรฉ Nussbaumer
  """
474 fb17bebd Renรฉ Nussbaumer
  if not (os.path.isabs(root) and os.path.isabs(other_path)):
475 fb17bebd Renรฉ Nussbaumer
    raise ValueError("Provided paths '%s' and '%s' are not absolute" %
476 fb17bebd Renรฉ Nussbaumer
                     (root, other_path))
477 fb17bebd Renรฉ Nussbaumer
  prepared_root = "%s%s" % (os.path.normpath(root), os.sep)
478 fb17bebd Renรฉ Nussbaumer
  return os.path.commonprefix([prepared_root,
479 fb17bebd Renรฉ Nussbaumer
                               os.path.normpath(other_path)]) == prepared_root
480 fb17bebd Renรฉ Nussbaumer
481 fb17bebd Renรฉ Nussbaumer
482 3865ca48 Michael Hanselmann
def PathJoin(*args):
483 3865ca48 Michael Hanselmann
  """Safe-join a list of path components.
484 3865ca48 Michael Hanselmann

485 3865ca48 Michael Hanselmann
  Requirements:
486 3865ca48 Michael Hanselmann
      - the first argument must be an absolute path
487 3865ca48 Michael Hanselmann
      - no component in the path must have backtracking (e.g. /../),
488 3865ca48 Michael Hanselmann
        since we check for normalization at the end
489 3865ca48 Michael Hanselmann

490 3865ca48 Michael Hanselmann
  @param args: the path components to be joined
491 3865ca48 Michael Hanselmann
  @raise ValueError: for invalid paths
492 3865ca48 Michael Hanselmann

493 3865ca48 Michael Hanselmann
  """
494 3865ca48 Michael Hanselmann
  # ensure we're having at least one path passed in
495 3865ca48 Michael Hanselmann
  assert args
496 3865ca48 Michael Hanselmann
  # ensure the first component is an absolute and normalized path name
497 3865ca48 Michael Hanselmann
  root = args[0]
498 3865ca48 Michael Hanselmann
  if not IsNormAbsPath(root):
499 3865ca48 Michael Hanselmann
    raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
500 3865ca48 Michael Hanselmann
  result = os.path.join(*args)
501 3865ca48 Michael Hanselmann
  # ensure that the whole path is normalized
502 3865ca48 Michael Hanselmann
  if not IsNormAbsPath(result):
503 3865ca48 Michael Hanselmann
    raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
504 3865ca48 Michael Hanselmann
  # check that we're still under the original prefix
505 cf00dba0 Renรฉ Nussbaumer
  if not IsBelowDir(root, result):
506 3865ca48 Michael Hanselmann
    raise ValueError("Error: path joining resulted in different prefix"
507 cf00dba0 Renรฉ Nussbaumer
                     " (%s != %s)" % (result, root))
508 3865ca48 Michael Hanselmann
  return result
509 3865ca48 Michael Hanselmann
510 3865ca48 Michael Hanselmann
511 3865ca48 Michael Hanselmann
def TailFile(fname, lines=20):
512 3865ca48 Michael Hanselmann
  """Return the last lines from a file.
513 3865ca48 Michael Hanselmann

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

518 3865ca48 Michael Hanselmann
  @param fname: the file name
519 3865ca48 Michael Hanselmann
  @type lines: int
520 3865ca48 Michael Hanselmann
  @param lines: the (maximum) number of lines to return
521 3865ca48 Michael Hanselmann

522 3865ca48 Michael Hanselmann
  """
523 3865ca48 Michael Hanselmann
  fd = open(fname, "r")
524 3865ca48 Michael Hanselmann
  try:
525 3865ca48 Michael Hanselmann
    fd.seek(0, 2)
526 3865ca48 Michael Hanselmann
    pos = fd.tell()
527 e687ec01 Michael Hanselmann
    pos = max(0, pos - 4096)
528 3865ca48 Michael Hanselmann
    fd.seek(pos, 0)
529 3865ca48 Michael Hanselmann
    raw_data = fd.read()
530 3865ca48 Michael Hanselmann
  finally:
531 3865ca48 Michael Hanselmann
    fd.close()
532 3865ca48 Michael Hanselmann
533 3865ca48 Michael Hanselmann
  rows = raw_data.splitlines()
534 3865ca48 Michael Hanselmann
  return rows[-lines:]
535 3865ca48 Michael Hanselmann
536 3865ca48 Michael Hanselmann
537 3865ca48 Michael Hanselmann
def BytesToMebibyte(value):
538 3865ca48 Michael Hanselmann
  """Converts bytes to mebibytes.
539 3865ca48 Michael Hanselmann

540 3865ca48 Michael Hanselmann
  @type value: int
541 3865ca48 Michael Hanselmann
  @param value: Value in bytes
542 3865ca48 Michael Hanselmann
  @rtype: int
543 3865ca48 Michael Hanselmann
  @return: Value in mebibytes
544 3865ca48 Michael Hanselmann

545 3865ca48 Michael Hanselmann
  """
546 3865ca48 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
547 3865ca48 Michael Hanselmann
548 3865ca48 Michael Hanselmann
549 3865ca48 Michael Hanselmann
def CalculateDirectorySize(path):
550 3865ca48 Michael Hanselmann
  """Calculates the size of a directory recursively.
551 3865ca48 Michael Hanselmann

552 3865ca48 Michael Hanselmann
  @type path: string
553 3865ca48 Michael Hanselmann
  @param path: Path to directory
554 3865ca48 Michael Hanselmann
  @rtype: int
555 3865ca48 Michael Hanselmann
  @return: Size in mebibytes
556 3865ca48 Michael Hanselmann

557 3865ca48 Michael Hanselmann
  """
558 3865ca48 Michael Hanselmann
  size = 0
559 3865ca48 Michael Hanselmann
560 3865ca48 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
561 3865ca48 Michael Hanselmann
    for filename in files:
562 3865ca48 Michael Hanselmann
      st = os.lstat(PathJoin(curpath, filename))
563 3865ca48 Michael Hanselmann
      size += st.st_size
564 3865ca48 Michael Hanselmann
565 3865ca48 Michael Hanselmann
  return BytesToMebibyte(size)
566 3865ca48 Michael Hanselmann
567 3865ca48 Michael Hanselmann
568 3865ca48 Michael Hanselmann
def GetFilesystemStats(path):
569 3865ca48 Michael Hanselmann
  """Returns the total and free space on a filesystem.
570 3865ca48 Michael Hanselmann

571 3865ca48 Michael Hanselmann
  @type path: string
572 3865ca48 Michael Hanselmann
  @param path: Path on filesystem to be examined
573 3865ca48 Michael Hanselmann
  @rtype: int
574 3865ca48 Michael Hanselmann
  @return: tuple of (Total space, Free space) in mebibytes
575 3865ca48 Michael Hanselmann

576 3865ca48 Michael Hanselmann
  """
577 3865ca48 Michael Hanselmann
  st = os.statvfs(path)
578 3865ca48 Michael Hanselmann
579 3865ca48 Michael Hanselmann
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
580 3865ca48 Michael Hanselmann
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
581 3865ca48 Michael Hanselmann
  return (tsize, fsize)
582 3865ca48 Michael Hanselmann
583 3865ca48 Michael Hanselmann
584 3865ca48 Michael Hanselmann
def ReadPidFile(pidfile):
585 3865ca48 Michael Hanselmann
  """Read a pid from a file.
586 3865ca48 Michael Hanselmann

587 3865ca48 Michael Hanselmann
  @type  pidfile: string
588 3865ca48 Michael Hanselmann
  @param pidfile: path to the file containing the pid
589 3865ca48 Michael Hanselmann
  @rtype: int
590 3865ca48 Michael Hanselmann
  @return: The process id, if the file exists and contains a valid PID,
591 3865ca48 Michael Hanselmann
           otherwise 0
592 3865ca48 Michael Hanselmann

593 3865ca48 Michael Hanselmann
  """
594 3865ca48 Michael Hanselmann
  try:
595 3865ca48 Michael Hanselmann
    raw_data = ReadOneLineFile(pidfile)
596 3865ca48 Michael Hanselmann
  except EnvironmentError, err:
597 3865ca48 Michael Hanselmann
    if err.errno != errno.ENOENT:
598 3865ca48 Michael Hanselmann
      logging.exception("Can't read pid file")
599 3865ca48 Michael Hanselmann
    return 0
600 3865ca48 Michael Hanselmann
601 3865ca48 Michael Hanselmann
  try:
602 3865ca48 Michael Hanselmann
    pid = int(raw_data)
603 3865ca48 Michael Hanselmann
  except (TypeError, ValueError), err:
604 3865ca48 Michael Hanselmann
    logging.info("Can't parse pid file contents", exc_info=True)
605 3865ca48 Michael Hanselmann
    return 0
606 3865ca48 Michael Hanselmann
607 3865ca48 Michael Hanselmann
  return pid
608 3865ca48 Michael Hanselmann
609 3865ca48 Michael Hanselmann
610 3865ca48 Michael Hanselmann
def ReadLockedPidFile(path):
611 3865ca48 Michael Hanselmann
  """Reads a locked PID file.
612 3865ca48 Michael Hanselmann

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

615 3865ca48 Michael Hanselmann
  @type path: string
616 3865ca48 Michael Hanselmann
  @param path: Path to PID file
617 3865ca48 Michael Hanselmann
  @return: PID as integer or, if file was unlocked or couldn't be opened, None
618 3865ca48 Michael Hanselmann

619 3865ca48 Michael Hanselmann
  """
620 3865ca48 Michael Hanselmann
  try:
621 3865ca48 Michael Hanselmann
    fd = os.open(path, os.O_RDONLY)
622 3865ca48 Michael Hanselmann
  except EnvironmentError, err:
623 3865ca48 Michael Hanselmann
    if err.errno == errno.ENOENT:
624 3865ca48 Michael Hanselmann
      # PID file doesn't exist
625 3865ca48 Michael Hanselmann
      return None
626 3865ca48 Michael Hanselmann
    raise
627 3865ca48 Michael Hanselmann
628 3865ca48 Michael Hanselmann
  try:
629 3865ca48 Michael Hanselmann
    try:
630 3865ca48 Michael Hanselmann
      # Try to acquire lock
631 3865ca48 Michael Hanselmann
      filelock.LockFile(fd)
632 3865ca48 Michael Hanselmann
    except errors.LockError:
633 3865ca48 Michael Hanselmann
      # Couldn't lock, daemon is running
634 3865ca48 Michael Hanselmann
      return int(os.read(fd, 100))
635 3865ca48 Michael Hanselmann
  finally:
636 3865ca48 Michael Hanselmann
    os.close(fd)
637 3865ca48 Michael Hanselmann
638 3865ca48 Michael Hanselmann
  return None
639 3865ca48 Michael Hanselmann
640 3865ca48 Michael Hanselmann
641 3865ca48 Michael Hanselmann
def AddAuthorizedKey(file_obj, key):
642 3865ca48 Michael Hanselmann
  """Adds an SSH public key to an authorized_keys file.
643 3865ca48 Michael Hanselmann

644 3865ca48 Michael Hanselmann
  @type file_obj: str or file handle
645 3865ca48 Michael Hanselmann
  @param file_obj: path to authorized_keys file
646 3865ca48 Michael Hanselmann
  @type key: str
647 3865ca48 Michael Hanselmann
  @param key: string containing key
648 3865ca48 Michael Hanselmann

649 3865ca48 Michael Hanselmann
  """
650 3865ca48 Michael Hanselmann
  key_fields = key.split()
651 3865ca48 Michael Hanselmann
652 3865ca48 Michael Hanselmann
  if isinstance(file_obj, basestring):
653 d0c8c01d Iustin Pop
    f = open(file_obj, "a+")
654 3865ca48 Michael Hanselmann
  else:
655 3865ca48 Michael Hanselmann
    f = file_obj
656 3865ca48 Michael Hanselmann
657 3865ca48 Michael Hanselmann
  try:
658 3865ca48 Michael Hanselmann
    nl = True
659 3865ca48 Michael Hanselmann
    for line in f:
660 3865ca48 Michael Hanselmann
      # Ignore whitespace changes
661 3865ca48 Michael Hanselmann
      if line.split() == key_fields:
662 3865ca48 Michael Hanselmann
        break
663 d0c8c01d Iustin Pop
      nl = line.endswith("\n")
664 3865ca48 Michael Hanselmann
    else:
665 3865ca48 Michael Hanselmann
      if not nl:
666 3865ca48 Michael Hanselmann
        f.write("\n")
667 d0c8c01d Iustin Pop
      f.write(key.rstrip("\r\n"))
668 3865ca48 Michael Hanselmann
      f.write("\n")
669 3865ca48 Michael Hanselmann
      f.flush()
670 3865ca48 Michael Hanselmann
  finally:
671 3865ca48 Michael Hanselmann
    f.close()
672 3865ca48 Michael Hanselmann
673 3865ca48 Michael Hanselmann
674 3865ca48 Michael Hanselmann
def RemoveAuthorizedKey(file_name, key):
675 3865ca48 Michael Hanselmann
  """Removes an SSH public key from an authorized_keys file.
676 3865ca48 Michael Hanselmann

677 3865ca48 Michael Hanselmann
  @type file_name: str
678 3865ca48 Michael Hanselmann
  @param file_name: path to authorized_keys file
679 3865ca48 Michael Hanselmann
  @type key: str
680 3865ca48 Michael Hanselmann
  @param key: string containing key
681 3865ca48 Michael Hanselmann

682 3865ca48 Michael Hanselmann
  """
683 3865ca48 Michael Hanselmann
  key_fields = key.split()
684 3865ca48 Michael Hanselmann
685 3865ca48 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
686 3865ca48 Michael Hanselmann
  try:
687 d0c8c01d Iustin Pop
    out = os.fdopen(fd, "w")
688 3865ca48 Michael Hanselmann
    try:
689 d0c8c01d Iustin Pop
      f = open(file_name, "r")
690 3865ca48 Michael Hanselmann
      try:
691 3865ca48 Michael Hanselmann
        for line in f:
692 3865ca48 Michael Hanselmann
          # Ignore whitespace changes while comparing lines
693 3865ca48 Michael Hanselmann
          if line.split() != key_fields:
694 3865ca48 Michael Hanselmann
            out.write(line)
695 3865ca48 Michael Hanselmann
696 3865ca48 Michael Hanselmann
        out.flush()
697 3865ca48 Michael Hanselmann
        os.rename(tmpname, file_name)
698 3865ca48 Michael Hanselmann
      finally:
699 3865ca48 Michael Hanselmann
        f.close()
700 3865ca48 Michael Hanselmann
    finally:
701 3865ca48 Michael Hanselmann
      out.close()
702 3865ca48 Michael Hanselmann
  except:
703 3865ca48 Michael Hanselmann
    RemoveFile(tmpname)
704 3865ca48 Michael Hanselmann
    raise
705 3865ca48 Michael Hanselmann
706 3865ca48 Michael Hanselmann
707 3865ca48 Michael Hanselmann
def DaemonPidFileName(name):
708 3865ca48 Michael Hanselmann
  """Compute a ganeti pid file absolute path
709 3865ca48 Michael Hanselmann

710 3865ca48 Michael Hanselmann
  @type name: str
711 3865ca48 Michael Hanselmann
  @param name: the daemon name
712 3865ca48 Michael Hanselmann
  @rtype: str
713 3865ca48 Michael Hanselmann
  @return: the full path to the pidfile corresponding to the given
714 3865ca48 Michael Hanselmann
      daemon name
715 3865ca48 Michael Hanselmann

716 3865ca48 Michael Hanselmann
  """
717 3865ca48 Michael Hanselmann
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
718 3865ca48 Michael Hanselmann
719 3865ca48 Michael Hanselmann
720 3865ca48 Michael Hanselmann
def WritePidFile(pidfile):
721 3865ca48 Michael Hanselmann
  """Write the current process pidfile.
722 3865ca48 Michael Hanselmann

723 3865ca48 Michael Hanselmann
  @type pidfile: string
724 3865ca48 Michael Hanselmann
  @param pidfile: the path to the file to be written
725 3865ca48 Michael Hanselmann
  @raise errors.LockError: if the pid file already exists and
726 3865ca48 Michael Hanselmann
      points to a live process
727 3865ca48 Michael Hanselmann
  @rtype: int
728 3865ca48 Michael Hanselmann
  @return: the file descriptor of the lock file; do not close this unless
729 3865ca48 Michael Hanselmann
      you want to unlock the pid file
730 3865ca48 Michael Hanselmann

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

750 3865ca48 Michael Hanselmann
  @type filename: string
751 3865ca48 Michael Hanselmann
  @param filename: Path to watcher pause file
752 3865ca48 Michael Hanselmann
  @type now: None, float or int
753 3865ca48 Michael Hanselmann
  @param now: Current time as Unix timestamp
754 3865ca48 Michael Hanselmann
  @type remove_after: int
755 3865ca48 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
756 3865ca48 Michael Hanselmann
    seconds past the pause end time
757 3865ca48 Michael Hanselmann

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

793 90e234a6 Michael Hanselmann
  @note: This is a Linux-specific method as it uses the /proc
794 90e234a6 Michael Hanselmann
      filesystem.
795 90e234a6 Michael Hanselmann
  @rtype: str
796 90e234a6 Michael Hanselmann

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