Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 099c52ad

History | View | Annotate | Download (63.3 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 58885d79 Iustin Pop
"""Ganeti utility module.
23 58885d79 Iustin Pop

24 58885d79 Iustin Pop
This module holds functions that can be used in both daemons (all) and
25 58885d79 Iustin Pop
the command line scripts.
26 899d2a81 Michael Hanselmann

27 a8083063 Iustin Pop
"""
28 a8083063 Iustin Pop
29 a8083063 Iustin Pop
30 a8083063 Iustin Pop
import os
31 a8083063 Iustin Pop
import time
32 113b55aa Iustin Pop
import subprocess
33 a8083063 Iustin Pop
import re
34 a8083063 Iustin Pop
import socket
35 a8083063 Iustin Pop
import tempfile
36 a8083063 Iustin Pop
import shutil
37 4ca1b175 Alexander Schreiber
import errno
38 2f8b60b3 Iustin Pop
import pwd
39 78feb6fb Guido Trotter
import itertools
40 9c233417 Iustin Pop
import select
41 9c233417 Iustin Pop
import fcntl
42 8f765069 Iustin Pop
import resource
43 bb698c1f Iustin Pop
import logging
44 de499029 Michael Hanselmann
import signal
45 9c233417 Iustin Pop
46 9c233417 Iustin Pop
from cStringIO import StringIO
47 a8083063 Iustin Pop
48 7ffe8fba Carlos Valiente
try:
49 7ffe8fba Carlos Valiente
  from hashlib import sha1
50 7ffe8fba Carlos Valiente
except ImportError:
51 7ffe8fba Carlos Valiente
  import sha
52 7ffe8fba Carlos Valiente
  sha1 = sha.new
53 7ffe8fba Carlos Valiente
54 a8083063 Iustin Pop
from ganeti import errors
55 3aecd2c7 Iustin Pop
from ganeti import constants
56 a8083063 Iustin Pop
57 16abfbc2 Alexander Schreiber
58 a8083063 Iustin Pop
_locksheld = []
59 a8083063 Iustin Pop
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
60 a8083063 Iustin Pop
61 e67bd559 Michael Hanselmann
debug_locks = False
62 58885d79 Iustin Pop
63 58885d79 Iustin Pop
#: when set to True, L{RunCmd} is disabled
64 b74159ee Iustin Pop
no_fork = False
65 f362096f Iustin Pop
66 13998ef2 Michael Hanselmann
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
67 13998ef2 Michael Hanselmann
68 7c0d6283 Michael Hanselmann
69 a8083063 Iustin Pop
class RunResult(object):
70 58885d79 Iustin Pop
  """Holds the result of running external programs.
71 58885d79 Iustin Pop

72 58885d79 Iustin Pop
  @type exit_code: int
73 58885d79 Iustin Pop
  @ivar exit_code: the exit code of the program, or None (if the program
74 58885d79 Iustin Pop
      didn't exit())
75 58885d79 Iustin Pop
  @type signal: int or None
76 58885d79 Iustin Pop
  @ivar signal: the signal that caused the program to finish, or None
77 58885d79 Iustin Pop
      (if the program wasn't terminated by a signal)
78 58885d79 Iustin Pop
  @type stdout: str
79 58885d79 Iustin Pop
  @ivar stdout: the standard output of the program
80 58885d79 Iustin Pop
  @type stderr: str
81 58885d79 Iustin Pop
  @ivar stderr: the standard error of the program
82 58885d79 Iustin Pop
  @type failed: boolean
83 58885d79 Iustin Pop
  @ivar failed: True in case the program was
84 58885d79 Iustin Pop
      terminated by a signal or exited with a non-zero exit code
85 58885d79 Iustin Pop
  @ivar fail_reason: a string detailing the termination reason
86 a8083063 Iustin Pop

87 a8083063 Iustin Pop
  """
88 a8083063 Iustin Pop
  __slots__ = ["exit_code", "signal", "stdout", "stderr",
89 a8083063 Iustin Pop
               "failed", "fail_reason", "cmd"]
90 a8083063 Iustin Pop
91 a8083063 Iustin Pop
92 38206f3c Iustin Pop
  def __init__(self, exit_code, signal_, stdout, stderr, cmd):
93 a8083063 Iustin Pop
    self.cmd = cmd
94 a8083063 Iustin Pop
    self.exit_code = exit_code
95 38206f3c Iustin Pop
    self.signal = signal_
96 a8083063 Iustin Pop
    self.stdout = stdout
97 a8083063 Iustin Pop
    self.stderr = stderr
98 38206f3c Iustin Pop
    self.failed = (signal_ is not None or exit_code != 0)
99 a8083063 Iustin Pop
100 a8083063 Iustin Pop
    if self.signal is not None:
101 a8083063 Iustin Pop
      self.fail_reason = "terminated by signal %s" % self.signal
102 a8083063 Iustin Pop
    elif self.exit_code is not None:
103 a8083063 Iustin Pop
      self.fail_reason = "exited with exit code %s" % self.exit_code
104 a8083063 Iustin Pop
    else:
105 a8083063 Iustin Pop
      self.fail_reason = "unable to determine termination reason"
106 a8083063 Iustin Pop
107 bb698c1f Iustin Pop
    if self.failed:
108 bb698c1f Iustin Pop
      logging.debug("Command '%s' failed (%s); output: %s",
109 bb698c1f Iustin Pop
                    self.cmd, self.fail_reason, self.output)
110 f362096f Iustin Pop
111 a8083063 Iustin Pop
  def _GetOutput(self):
112 a8083063 Iustin Pop
    """Returns the combined stdout and stderr for easier usage.
113 a8083063 Iustin Pop

114 a8083063 Iustin Pop
    """
115 a8083063 Iustin Pop
    return self.stdout + self.stderr
116 a8083063 Iustin Pop
117 a8083063 Iustin Pop
  output = property(_GetOutput, None, None, "Return full output")
118 a8083063 Iustin Pop
119 a8083063 Iustin Pop
120 8797df43 Iustin Pop
def RunCmd(cmd, env=None, output=None, cwd='/'):
121 a8083063 Iustin Pop
  """Execute a (shell) command.
122 a8083063 Iustin Pop

123 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
124 a8083063 Iustin Pop
  closed.
125 a8083063 Iustin Pop

126 e326d4e5 Guido Trotter
  @type  cmd: string or list
127 36117c2b Iustin Pop
  @param cmd: Command to run
128 2557ff82 Guido Trotter
  @type env: dict
129 58885d79 Iustin Pop
  @param env: Additional environment
130 36117c2b Iustin Pop
  @type output: str
131 58885d79 Iustin Pop
  @param output: if desired, the output of the command can be
132 36117c2b Iustin Pop
      saved in a file instead of the RunResult instance; this
133 36117c2b Iustin Pop
      parameter denotes the file name (if not None)
134 8797df43 Iustin Pop
  @type cwd: string
135 8797df43 Iustin Pop
  @param cwd: if specified, will be used as the working
136 8797df43 Iustin Pop
      directory for the command; the default will be /
137 36117c2b Iustin Pop
  @rtype: L{RunResult}
138 58885d79 Iustin Pop
  @return: RunResult instance
139 5bbd3f7f Michael Hanselmann
  @raise errors.ProgrammerError: if we call this when forks are disabled
140 a8083063 Iustin Pop

141 a8083063 Iustin Pop
  """
142 b74159ee Iustin Pop
  if no_fork:
143 b74159ee Iustin Pop
    raise errors.ProgrammerError("utils.RunCmd() called with fork() disabled")
144 b74159ee Iustin Pop
145 a8083063 Iustin Pop
  if isinstance(cmd, list):
146 a8083063 Iustin Pop
    cmd = [str(val) for val in cmd]
147 113b55aa Iustin Pop
    strcmd = " ".join(cmd)
148 113b55aa Iustin Pop
    shell = False
149 113b55aa Iustin Pop
  else:
150 113b55aa Iustin Pop
    strcmd = cmd
151 113b55aa Iustin Pop
    shell = True
152 bb698c1f Iustin Pop
  logging.debug("RunCmd '%s'", strcmd)
153 2557ff82 Guido Trotter
154 2557ff82 Guido Trotter
  cmd_env = os.environ.copy()
155 2557ff82 Guido Trotter
  cmd_env["LC_ALL"] = "C"
156 2557ff82 Guido Trotter
  if env is not None:
157 2557ff82 Guido Trotter
    cmd_env.update(env)
158 2557ff82 Guido Trotter
159 c803b052 Iustin Pop
  try:
160 c803b052 Iustin Pop
    if output is None:
161 c803b052 Iustin Pop
      out, err, status = _RunCmdPipe(cmd, cmd_env, shell, cwd)
162 c803b052 Iustin Pop
    else:
163 c803b052 Iustin Pop
      status = _RunCmdFile(cmd, cmd_env, shell, output, cwd)
164 c803b052 Iustin Pop
      out = err = ""
165 c803b052 Iustin Pop
  except OSError, err:
166 c803b052 Iustin Pop
    if err.errno == errno.ENOENT:
167 c803b052 Iustin Pop
      raise errors.OpExecError("Can't execute '%s': not found (%s)" %
168 c803b052 Iustin Pop
                               (strcmd, err))
169 c803b052 Iustin Pop
    else:
170 c803b052 Iustin Pop
      raise
171 36117c2b Iustin Pop
172 36117c2b Iustin Pop
  if status >= 0:
173 36117c2b Iustin Pop
    exitcode = status
174 36117c2b Iustin Pop
    signal_ = None
175 36117c2b Iustin Pop
  else:
176 36117c2b Iustin Pop
    exitcode = None
177 36117c2b Iustin Pop
    signal_ = -status
178 36117c2b Iustin Pop
179 36117c2b Iustin Pop
  return RunResult(exitcode, signal_, out, err, strcmd)
180 36117c2b Iustin Pop
181 ae59efea Michael Hanselmann
182 8797df43 Iustin Pop
def _RunCmdPipe(cmd, env, via_shell, cwd):
183 36117c2b Iustin Pop
  """Run a command and return its output.
184 36117c2b Iustin Pop

185 36117c2b Iustin Pop
  @type  cmd: string or list
186 36117c2b Iustin Pop
  @param cmd: Command to run
187 36117c2b Iustin Pop
  @type env: dict
188 36117c2b Iustin Pop
  @param env: The environment to use
189 36117c2b Iustin Pop
  @type via_shell: bool
190 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
191 8797df43 Iustin Pop
  @type cwd: string
192 8797df43 Iustin Pop
  @param cwd: the working directory for the program
193 36117c2b Iustin Pop
  @rtype: tuple
194 36117c2b Iustin Pop
  @return: (out, err, status)
195 36117c2b Iustin Pop

196 36117c2b Iustin Pop
  """
197 9c233417 Iustin Pop
  poller = select.poll()
198 36117c2b Iustin Pop
  child = subprocess.Popen(cmd, shell=via_shell,
199 113b55aa Iustin Pop
                           stderr=subprocess.PIPE,
200 113b55aa Iustin Pop
                           stdout=subprocess.PIPE,
201 113b55aa Iustin Pop
                           stdin=subprocess.PIPE,
202 8797df43 Iustin Pop
                           close_fds=True, env=env,
203 8797df43 Iustin Pop
                           cwd=cwd)
204 113b55aa Iustin Pop
205 113b55aa Iustin Pop
  child.stdin.close()
206 9c233417 Iustin Pop
  poller.register(child.stdout, select.POLLIN)
207 9c233417 Iustin Pop
  poller.register(child.stderr, select.POLLIN)
208 9c233417 Iustin Pop
  out = StringIO()
209 9c233417 Iustin Pop
  err = StringIO()
210 9c233417 Iustin Pop
  fdmap = {
211 9c233417 Iustin Pop
    child.stdout.fileno(): (out, child.stdout),
212 9c233417 Iustin Pop
    child.stderr.fileno(): (err, child.stderr),
213 9c233417 Iustin Pop
    }
214 9c233417 Iustin Pop
  for fd in fdmap:
215 9c233417 Iustin Pop
    status = fcntl.fcntl(fd, fcntl.F_GETFL)
216 9c233417 Iustin Pop
    fcntl.fcntl(fd, fcntl.F_SETFL, status | os.O_NONBLOCK)
217 9c233417 Iustin Pop
218 9c233417 Iustin Pop
  while fdmap:
219 bf988c29 Guido Trotter
    try:
220 bf988c29 Guido Trotter
      pollresult = poller.poll()
221 bf988c29 Guido Trotter
    except EnvironmentError, eerr:
222 bf988c29 Guido Trotter
      if eerr.errno == errno.EINTR:
223 bf988c29 Guido Trotter
        continue
224 bf988c29 Guido Trotter
      raise
225 bf988c29 Guido Trotter
    except select.error, serr:
226 bf988c29 Guido Trotter
      if serr[0] == errno.EINTR:
227 bf988c29 Guido Trotter
        continue
228 bf988c29 Guido Trotter
      raise
229 bf988c29 Guido Trotter
230 bf988c29 Guido Trotter
    for fd, event in pollresult:
231 9c233417 Iustin Pop
      if event & select.POLLIN or event & select.POLLPRI:
232 9c233417 Iustin Pop
        data = fdmap[fd][1].read()
233 9c233417 Iustin Pop
        # no data from read signifies EOF (the same as POLLHUP)
234 9c233417 Iustin Pop
        if not data:
235 9c233417 Iustin Pop
          poller.unregister(fd)
236 9c233417 Iustin Pop
          del fdmap[fd]
237 9c233417 Iustin Pop
          continue
238 9c233417 Iustin Pop
        fdmap[fd][0].write(data)
239 9c233417 Iustin Pop
      if (event & select.POLLNVAL or event & select.POLLHUP or
240 9c233417 Iustin Pop
          event & select.POLLERR):
241 9c233417 Iustin Pop
        poller.unregister(fd)
242 9c233417 Iustin Pop
        del fdmap[fd]
243 9c233417 Iustin Pop
244 9c233417 Iustin Pop
  out = out.getvalue()
245 9c233417 Iustin Pop
  err = err.getvalue()
246 a8083063 Iustin Pop
247 a8083063 Iustin Pop
  status = child.wait()
248 36117c2b Iustin Pop
  return out, err, status
249 a8083063 Iustin Pop
250 36117c2b Iustin Pop
251 8797df43 Iustin Pop
def _RunCmdFile(cmd, env, via_shell, output, cwd):
252 36117c2b Iustin Pop
  """Run a command and save its output to a file.
253 36117c2b Iustin Pop

254 36117c2b Iustin Pop
  @type  cmd: string or list
255 36117c2b Iustin Pop
  @param cmd: Command to run
256 36117c2b Iustin Pop
  @type env: dict
257 36117c2b Iustin Pop
  @param env: The environment to use
258 36117c2b Iustin Pop
  @type via_shell: bool
259 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
260 36117c2b Iustin Pop
  @type output: str
261 36117c2b Iustin Pop
  @param output: the filename in which to save the output
262 8797df43 Iustin Pop
  @type cwd: string
263 8797df43 Iustin Pop
  @param cwd: the working directory for the program
264 36117c2b Iustin Pop
  @rtype: int
265 36117c2b Iustin Pop
  @return: the exit status
266 36117c2b Iustin Pop

267 36117c2b Iustin Pop
  """
268 36117c2b Iustin Pop
  fh = open(output, "a")
269 36117c2b Iustin Pop
  try:
270 36117c2b Iustin Pop
    child = subprocess.Popen(cmd, shell=via_shell,
271 36117c2b Iustin Pop
                             stderr=subprocess.STDOUT,
272 36117c2b Iustin Pop
                             stdout=fh,
273 36117c2b Iustin Pop
                             stdin=subprocess.PIPE,
274 8797df43 Iustin Pop
                             close_fds=True, env=env,
275 8797df43 Iustin Pop
                             cwd=cwd)
276 36117c2b Iustin Pop
277 36117c2b Iustin Pop
    child.stdin.close()
278 36117c2b Iustin Pop
    status = child.wait()
279 36117c2b Iustin Pop
  finally:
280 36117c2b Iustin Pop
    fh.close()
281 36117c2b Iustin Pop
  return status
282 a8083063 Iustin Pop
283 a8083063 Iustin Pop
284 a8083063 Iustin Pop
def RemoveFile(filename):
285 a8083063 Iustin Pop
  """Remove a file ignoring some errors.
286 a8083063 Iustin Pop

287 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
288 a8083063 Iustin Pop
  errors are passed.
289 a8083063 Iustin Pop

290 58885d79 Iustin Pop
  @type filename: str
291 58885d79 Iustin Pop
  @param filename: the file to be removed
292 58885d79 Iustin Pop

293 a8083063 Iustin Pop
  """
294 a8083063 Iustin Pop
  try:
295 a8083063 Iustin Pop
    os.unlink(filename)
296 a8083063 Iustin Pop
  except OSError, err:
297 4ca1b175 Alexander Schreiber
    if err.errno not in (errno.ENOENT, errno.EISDIR):
298 a8083063 Iustin Pop
      raise
299 a8083063 Iustin Pop
300 a8083063 Iustin Pop
301 6e797216 Michael Hanselmann
def RenameFile(old, new, mkdir=False, mkdir_mode=0750):
302 6e797216 Michael Hanselmann
  """Renames a file.
303 6e797216 Michael Hanselmann

304 6e797216 Michael Hanselmann
  @type old: string
305 6e797216 Michael Hanselmann
  @param old: Original path
306 6e797216 Michael Hanselmann
  @type new: string
307 6e797216 Michael Hanselmann
  @param new: New path
308 6e797216 Michael Hanselmann
  @type mkdir: bool
309 6e797216 Michael Hanselmann
  @param mkdir: Whether to create target directory if it doesn't exist
310 6e797216 Michael Hanselmann
  @type mkdir_mode: int
311 6e797216 Michael Hanselmann
  @param mkdir_mode: Mode for newly created directories
312 6e797216 Michael Hanselmann

313 6e797216 Michael Hanselmann
  """
314 6e797216 Michael Hanselmann
  try:
315 6e797216 Michael Hanselmann
    return os.rename(old, new)
316 6e797216 Michael Hanselmann
  except OSError, err:
317 6e797216 Michael Hanselmann
    # In at least one use case of this function, the job queue, directory
318 6e797216 Michael Hanselmann
    # creation is very rare. Checking for the directory before renaming is not
319 6e797216 Michael Hanselmann
    # as efficient.
320 6e797216 Michael Hanselmann
    if mkdir and err.errno == errno.ENOENT:
321 6e797216 Michael Hanselmann
      # Create directory and try again
322 6e797216 Michael Hanselmann
      os.makedirs(os.path.dirname(new), mkdir_mode)
323 6e797216 Michael Hanselmann
      return os.rename(old, new)
324 6e797216 Michael Hanselmann
    raise
325 6e797216 Michael Hanselmann
326 6e797216 Michael Hanselmann
327 a8083063 Iustin Pop
def _FingerprintFile(filename):
328 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
329 a8083063 Iustin Pop

330 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
331 a8083063 Iustin Pop
  instead.
332 a8083063 Iustin Pop

333 58885d79 Iustin Pop
  @type filename: str
334 58885d79 Iustin Pop
  @param filename: the filename to checksum
335 58885d79 Iustin Pop
  @rtype: str
336 58885d79 Iustin Pop
  @return: the hex digest of the sha checksum of the contents
337 58885d79 Iustin Pop
      of the file
338 a8083063 Iustin Pop

339 a8083063 Iustin Pop
  """
340 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
341 a8083063 Iustin Pop
    return None
342 a8083063 Iustin Pop
343 a8083063 Iustin Pop
  f = open(filename)
344 a8083063 Iustin Pop
345 7ffe8fba Carlos Valiente
  fp = sha1()
346 a8083063 Iustin Pop
  while True:
347 a8083063 Iustin Pop
    data = f.read(4096)
348 a8083063 Iustin Pop
    if not data:
349 a8083063 Iustin Pop
      break
350 a8083063 Iustin Pop
351 a8083063 Iustin Pop
    fp.update(data)
352 a8083063 Iustin Pop
353 a8083063 Iustin Pop
  return fp.hexdigest()
354 a8083063 Iustin Pop
355 a8083063 Iustin Pop
356 a8083063 Iustin Pop
def FingerprintFiles(files):
357 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
358 a8083063 Iustin Pop

359 58885d79 Iustin Pop
  @type files: list
360 58885d79 Iustin Pop
  @param files: the list of filename to fingerprint
361 58885d79 Iustin Pop
  @rtype: dict
362 58885d79 Iustin Pop
  @return: a dictionary filename: fingerprint, holding only
363 58885d79 Iustin Pop
      existing files
364 a8083063 Iustin Pop

365 a8083063 Iustin Pop
  """
366 a8083063 Iustin Pop
  ret = {}
367 a8083063 Iustin Pop
368 a8083063 Iustin Pop
  for filename in files:
369 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
370 a8083063 Iustin Pop
    if cksum:
371 a8083063 Iustin Pop
      ret[filename] = cksum
372 a8083063 Iustin Pop
373 a8083063 Iustin Pop
  return ret
374 a8083063 Iustin Pop
375 a8083063 Iustin Pop
376 a5728081 Guido Trotter
def ForceDictType(target, key_types, allowed_values=None):
377 a5728081 Guido Trotter
  """Force the values of a dict to have certain types.
378 a5728081 Guido Trotter

379 a5728081 Guido Trotter
  @type target: dict
380 a5728081 Guido Trotter
  @param target: the dict to update
381 a5728081 Guido Trotter
  @type key_types: dict
382 a5728081 Guido Trotter
  @param key_types: dict mapping target dict keys to types
383 a5728081 Guido Trotter
                    in constants.ENFORCEABLE_TYPES
384 a5728081 Guido Trotter
  @type allowed_values: list
385 a5728081 Guido Trotter
  @keyword allowed_values: list of specially allowed values
386 a5728081 Guido Trotter

387 a5728081 Guido Trotter
  """
388 a5728081 Guido Trotter
  if allowed_values is None:
389 a5728081 Guido Trotter
    allowed_values = []
390 a5728081 Guido Trotter
391 8b46606c Guido Trotter
  if not isinstance(target, dict):
392 8b46606c Guido Trotter
    msg = "Expected dictionary, got '%s'" % target
393 8b46606c Guido Trotter
    raise errors.TypeEnforcementError(msg)
394 8b46606c Guido Trotter
395 a5728081 Guido Trotter
  for key in target:
396 a5728081 Guido Trotter
    if key not in key_types:
397 a5728081 Guido Trotter
      msg = "Unknown key '%s'" % key
398 a5728081 Guido Trotter
      raise errors.TypeEnforcementError(msg)
399 a5728081 Guido Trotter
400 a5728081 Guido Trotter
    if target[key] in allowed_values:
401 a5728081 Guido Trotter
      continue
402 a5728081 Guido Trotter
403 29921401 Iustin Pop
    ktype = key_types[key]
404 29921401 Iustin Pop
    if ktype not in constants.ENFORCEABLE_TYPES:
405 29921401 Iustin Pop
      msg = "'%s' has non-enforceable type %s" % (key, ktype)
406 a5728081 Guido Trotter
      raise errors.ProgrammerError(msg)
407 a5728081 Guido Trotter
408 29921401 Iustin Pop
    if ktype == constants.VTYPE_STRING:
409 a5728081 Guido Trotter
      if not isinstance(target[key], basestring):
410 a5728081 Guido Trotter
        if isinstance(target[key], bool) and not target[key]:
411 a5728081 Guido Trotter
          target[key] = ''
412 a5728081 Guido Trotter
        else:
413 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid string" % (key, target[key])
414 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
415 29921401 Iustin Pop
    elif ktype == constants.VTYPE_BOOL:
416 a5728081 Guido Trotter
      if isinstance(target[key], basestring) and target[key]:
417 a5728081 Guido Trotter
        if target[key].lower() == constants.VALUE_FALSE:
418 a5728081 Guido Trotter
          target[key] = False
419 a5728081 Guido Trotter
        elif target[key].lower() == constants.VALUE_TRUE:
420 a5728081 Guido Trotter
          target[key] = True
421 a5728081 Guido Trotter
        else:
422 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid boolean" % (key, target[key])
423 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
424 a5728081 Guido Trotter
      elif target[key]:
425 a5728081 Guido Trotter
        target[key] = True
426 a5728081 Guido Trotter
      else:
427 a5728081 Guido Trotter
        target[key] = False
428 29921401 Iustin Pop
    elif ktype == constants.VTYPE_SIZE:
429 a5728081 Guido Trotter
      try:
430 a5728081 Guido Trotter
        target[key] = ParseUnit(target[key])
431 a5728081 Guido Trotter
      except errors.UnitParseError, err:
432 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid size. error: %s" % \
433 a5728081 Guido Trotter
              (key, target[key], err)
434 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
435 29921401 Iustin Pop
    elif ktype == constants.VTYPE_INT:
436 a5728081 Guido Trotter
      try:
437 a5728081 Guido Trotter
        target[key] = int(target[key])
438 a5728081 Guido Trotter
      except (ValueError, TypeError):
439 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid integer" % (key, target[key])
440 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
441 a5728081 Guido Trotter
442 a5728081 Guido Trotter
443 a8083063 Iustin Pop
def IsProcessAlive(pid):
444 a8083063 Iustin Pop
  """Check if a given pid exists on the system.
445 a8083063 Iustin Pop

446 44bf25ff Iustin Pop
  @note: zombie status is not handled, so zombie processes
447 44bf25ff Iustin Pop
      will be returned as alive
448 58885d79 Iustin Pop
  @type pid: int
449 58885d79 Iustin Pop
  @param pid: the process ID to check
450 58885d79 Iustin Pop
  @rtype: boolean
451 58885d79 Iustin Pop
  @return: True if the process exists
452 a8083063 Iustin Pop

453 a8083063 Iustin Pop
  """
454 d9f311d7 Iustin Pop
  if pid <= 0:
455 d9f311d7 Iustin Pop
    return False
456 d9f311d7 Iustin Pop
457 a8083063 Iustin Pop
  try:
458 44bf25ff Iustin Pop
    os.stat("/proc/%d/status" % pid)
459 44bf25ff Iustin Pop
    return True
460 44bf25ff Iustin Pop
  except EnvironmentError, err:
461 4ca1b175 Alexander Schreiber
    if err.errno in (errno.ENOENT, errno.ENOTDIR):
462 a8083063 Iustin Pop
      return False
463 44bf25ff Iustin Pop
    raise
464 a8083063 Iustin Pop
465 a8083063 Iustin Pop
466 d9f311d7 Iustin Pop
def ReadPidFile(pidfile):
467 58885d79 Iustin Pop
  """Read a pid from a file.
468 fee80e90 Guido Trotter

469 58885d79 Iustin Pop
  @type  pidfile: string
470 58885d79 Iustin Pop
  @param pidfile: path to the file containing the pid
471 58885d79 Iustin Pop
  @rtype: int
472 1de62f37 Guido Trotter
  @return: The process id, if the file exists and contains a valid PID,
473 d9f311d7 Iustin Pop
           otherwise 0
474 fee80e90 Guido Trotter

475 fee80e90 Guido Trotter
  """
476 fee80e90 Guido Trotter
  try:
477 13998ef2 Michael Hanselmann
    raw_data = ReadFile(pidfile)
478 d9f311d7 Iustin Pop
  except EnvironmentError, err:
479 d9f311d7 Iustin Pop
    if err.errno != errno.ENOENT:
480 13998ef2 Michael Hanselmann
      logging.exception("Can't read pid file")
481 d9f311d7 Iustin Pop
    return 0
482 fee80e90 Guido Trotter
483 fee80e90 Guido Trotter
  try:
484 13998ef2 Michael Hanselmann
    pid = int(raw_data)
485 d9f311d7 Iustin Pop
  except ValueError, err:
486 8161a646 Iustin Pop
    logging.info("Can't parse pid file contents", exc_info=True)
487 d9f311d7 Iustin Pop
    return 0
488 fee80e90 Guido Trotter
489 d9f311d7 Iustin Pop
  return pid
490 fee80e90 Guido Trotter
491 fee80e90 Guido Trotter
492 256eb94b Guido Trotter
def MatchNameComponent(key, name_list, case_sensitive=True):
493 a8083063 Iustin Pop
  """Try to match a name against a list.
494 a8083063 Iustin Pop

495 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
496 58885d79 Iustin Pop
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
497 58885d79 Iustin Pop
  this list, I{'test1'} as well as I{'test1.example'} will match, but
498 58885d79 Iustin Pop
  not I{'test1.ex'}. A multiple match will be considered as no match
499 58885d79 Iustin Pop
  at all (e.g. I{'test1'} against C{['test1.example.com',
500 3a541d90 Iustin Pop
  'test1.example.org']}), except when the key fully matches an entry
501 3a541d90 Iustin Pop
  (e.g. I{'test1'} against C{['test1', 'test1.example.com']}).
502 a8083063 Iustin Pop

503 58885d79 Iustin Pop
  @type key: str
504 58885d79 Iustin Pop
  @param key: the name to be searched
505 58885d79 Iustin Pop
  @type name_list: list
506 58885d79 Iustin Pop
  @param name_list: the list of strings against which to search the key
507 256eb94b Guido Trotter
  @type case_sensitive: boolean
508 256eb94b Guido Trotter
  @param case_sensitive: whether to provide a case-sensitive match
509 a8083063 Iustin Pop

510 58885d79 Iustin Pop
  @rtype: None or str
511 58885d79 Iustin Pop
  @return: None if there is no match I{or} if there are multiple matches,
512 58885d79 Iustin Pop
      otherwise the element from the list which matches
513 a8083063 Iustin Pop

514 a8083063 Iustin Pop
  """
515 3a541d90 Iustin Pop
  if key in name_list:
516 3a541d90 Iustin Pop
    return key
517 256eb94b Guido Trotter
518 256eb94b Guido Trotter
  re_flags = 0
519 256eb94b Guido Trotter
  if not case_sensitive:
520 256eb94b Guido Trotter
    re_flags |= re.IGNORECASE
521 099c52ad Iustin Pop
    key = key.upper()
522 256eb94b Guido Trotter
  mo = re.compile("^%s(\..*)?$" % re.escape(key), re_flags)
523 256eb94b Guido Trotter
  names_filtered = []
524 256eb94b Guido Trotter
  string_matches = []
525 256eb94b Guido Trotter
  for name in name_list:
526 256eb94b Guido Trotter
    if mo.match(name) is not None:
527 256eb94b Guido Trotter
      names_filtered.append(name)
528 099c52ad Iustin Pop
      if not case_sensitive and key == name.upper():
529 256eb94b Guido Trotter
        string_matches.append(name)
530 256eb94b Guido Trotter
531 256eb94b Guido Trotter
  if len(string_matches) == 1:
532 256eb94b Guido Trotter
    return string_matches[0]
533 256eb94b Guido Trotter
  if len(names_filtered) == 1:
534 256eb94b Guido Trotter
    return names_filtered[0]
535 256eb94b Guido Trotter
  return None
536 a8083063 Iustin Pop
537 a8083063 Iustin Pop
538 bcf043c9 Iustin Pop
class HostInfo:
539 89e1fc26 Iustin Pop
  """Class implementing resolver and hostname functionality
540 bcf043c9 Iustin Pop

541 bcf043c9 Iustin Pop
  """
542 89e1fc26 Iustin Pop
  def __init__(self, name=None):
543 bcf043c9 Iustin Pop
    """Initialize the host name object.
544 bcf043c9 Iustin Pop

545 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
546 89e1fc26 Iustin Pop
    name.
547 bcf043c9 Iustin Pop

548 bcf043c9 Iustin Pop
    """
549 89e1fc26 Iustin Pop
    if name is None:
550 89e1fc26 Iustin Pop
      name = self.SysName()
551 89e1fc26 Iustin Pop
552 89e1fc26 Iustin Pop
    self.query = name
553 89e1fc26 Iustin Pop
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
554 bcf043c9 Iustin Pop
    self.ip = self.ipaddrs[0]
555 bcf043c9 Iustin Pop
556 c8a0948f Michael Hanselmann
  def ShortName(self):
557 c8a0948f Michael Hanselmann
    """Returns the hostname without domain.
558 c8a0948f Michael Hanselmann

559 c8a0948f Michael Hanselmann
    """
560 c8a0948f Michael Hanselmann
    return self.name.split('.')[0]
561 c8a0948f Michael Hanselmann
562 89e1fc26 Iustin Pop
  @staticmethod
563 89e1fc26 Iustin Pop
  def SysName():
564 89e1fc26 Iustin Pop
    """Return the current system's name.
565 bcf043c9 Iustin Pop

566 58885d79 Iustin Pop
    This is simply a wrapper over C{socket.gethostname()}.
567 a8083063 Iustin Pop

568 89e1fc26 Iustin Pop
    """
569 89e1fc26 Iustin Pop
    return socket.gethostname()
570 a8083063 Iustin Pop
571 89e1fc26 Iustin Pop
  @staticmethod
572 89e1fc26 Iustin Pop
  def LookupHostname(hostname):
573 89e1fc26 Iustin Pop
    """Look up hostname
574 a8083063 Iustin Pop

575 58885d79 Iustin Pop
    @type hostname: str
576 58885d79 Iustin Pop
    @param hostname: hostname to look up
577 89e1fc26 Iustin Pop

578 58885d79 Iustin Pop
    @rtype: tuple
579 58885d79 Iustin Pop
    @return: a tuple (name, aliases, ipaddrs) as returned by
580 58885d79 Iustin Pop
        C{socket.gethostbyname_ex}
581 58885d79 Iustin Pop
    @raise errors.ResolverError: in case of errors in resolving
582 89e1fc26 Iustin Pop

583 89e1fc26 Iustin Pop
    """
584 89e1fc26 Iustin Pop
    try:
585 89e1fc26 Iustin Pop
      result = socket.gethostbyname_ex(hostname)
586 89e1fc26 Iustin Pop
    except socket.gaierror, err:
587 89e1fc26 Iustin Pop
      # hostname not found in DNS
588 89e1fc26 Iustin Pop
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
589 a8083063 Iustin Pop
590 89e1fc26 Iustin Pop
    return result
591 a8083063 Iustin Pop
592 a8083063 Iustin Pop
593 a8083063 Iustin Pop
def ListVolumeGroups():
594 a8083063 Iustin Pop
  """List volume groups and their size
595 a8083063 Iustin Pop

596 58885d79 Iustin Pop
  @rtype: dict
597 58885d79 Iustin Pop
  @return:
598 58885d79 Iustin Pop
       Dictionary with keys volume name and values
599 58885d79 Iustin Pop
       the size of the volume
600 a8083063 Iustin Pop

601 a8083063 Iustin Pop
  """
602 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
603 a8083063 Iustin Pop
  result = RunCmd(command)
604 a8083063 Iustin Pop
  retval = {}
605 a8083063 Iustin Pop
  if result.failed:
606 a8083063 Iustin Pop
    return retval
607 a8083063 Iustin Pop
608 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
609 a8083063 Iustin Pop
    try:
610 a8083063 Iustin Pop
      name, size = line.split()
611 a8083063 Iustin Pop
      size = int(float(size))
612 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
613 bb698c1f Iustin Pop
      logging.error("Invalid output from vgs (%s): %s", err, line)
614 a8083063 Iustin Pop
      continue
615 a8083063 Iustin Pop
616 a8083063 Iustin Pop
    retval[name] = size
617 a8083063 Iustin Pop
618 a8083063 Iustin Pop
  return retval
619 a8083063 Iustin Pop
620 a8083063 Iustin Pop
621 a8083063 Iustin Pop
def BridgeExists(bridge):
622 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
623 a8083063 Iustin Pop

624 58885d79 Iustin Pop
  @type bridge: str
625 58885d79 Iustin Pop
  @param bridge: the bridge name to check
626 58885d79 Iustin Pop
  @rtype: boolean
627 58885d79 Iustin Pop
  @return: True if it does
628 a8083063 Iustin Pop

629 a8083063 Iustin Pop
  """
630 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
631 a8083063 Iustin Pop
632 a8083063 Iustin Pop
633 a8083063 Iustin Pop
def NiceSort(name_list):
634 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
635 a8083063 Iustin Pop

636 58885d79 Iustin Pop
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
637 58885d79 Iustin Pop
  will sort the list in the logical order C{['a1', 'a2', 'a10',
638 58885d79 Iustin Pop
  'a11']}.
639 a8083063 Iustin Pop

640 a8083063 Iustin Pop
  The sort algorithm breaks each name in groups of either only-digits
641 a8083063 Iustin Pop
  or no-digits. Only the first eight such groups are considered, and
642 a8083063 Iustin Pop
  after that we just use what's left of the string.
643 a8083063 Iustin Pop

644 58885d79 Iustin Pop
  @type name_list: list
645 58885d79 Iustin Pop
  @param name_list: the names to be sorted
646 58885d79 Iustin Pop
  @rtype: list
647 58885d79 Iustin Pop
  @return: a copy of the name list sorted with our algorithm
648 a8083063 Iustin Pop

649 a8083063 Iustin Pop
  """
650 a8083063 Iustin Pop
  _SORTER_BASE = "(\D+|\d+)"
651 a8083063 Iustin Pop
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
652 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
653 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
654 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE)
655 a8083063 Iustin Pop
  _SORTER_RE = re.compile(_SORTER_FULL)
656 a8083063 Iustin Pop
  _SORTER_NODIGIT = re.compile("^\D*$")
657 a8083063 Iustin Pop
  def _TryInt(val):
658 a8083063 Iustin Pop
    """Attempts to convert a variable to integer."""
659 a8083063 Iustin Pop
    if val is None or _SORTER_NODIGIT.match(val):
660 a8083063 Iustin Pop
      return val
661 a8083063 Iustin Pop
    rval = int(val)
662 a8083063 Iustin Pop
    return rval
663 a8083063 Iustin Pop
664 a8083063 Iustin Pop
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
665 a8083063 Iustin Pop
             for name in name_list]
666 a8083063 Iustin Pop
  to_sort.sort()
667 a8083063 Iustin Pop
  return [tup[1] for tup in to_sort]
668 a8083063 Iustin Pop
669 a8083063 Iustin Pop
670 a8083063 Iustin Pop
def TryConvert(fn, val):
671 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
672 a8083063 Iustin Pop

673 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
674 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
675 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
676 58885d79 Iustin Pop
  exceptions are propagated to the caller.
677 58885d79 Iustin Pop

678 58885d79 Iustin Pop
  @type fn: callable
679 58885d79 Iustin Pop
  @param fn: function to apply to the value
680 58885d79 Iustin Pop
  @param val: the value to be converted
681 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
682 58885d79 Iustin Pop
      otherwise the original value.
683 a8083063 Iustin Pop

684 a8083063 Iustin Pop
  """
685 a8083063 Iustin Pop
  try:
686 a8083063 Iustin Pop
    nv = fn(val)
687 7c4d6c7b Michael Hanselmann
  except (ValueError, TypeError):
688 a8083063 Iustin Pop
    nv = val
689 a8083063 Iustin Pop
  return nv
690 a8083063 Iustin Pop
691 a8083063 Iustin Pop
692 a8083063 Iustin Pop
def IsValidIP(ip):
693 58885d79 Iustin Pop
  """Verifies the syntax of an IPv4 address.
694 a8083063 Iustin Pop

695 58885d79 Iustin Pop
  This function checks if the IPv4 address passes is valid or not based
696 58885d79 Iustin Pop
  on syntax (not IP range, class calculations, etc.).
697 58885d79 Iustin Pop

698 58885d79 Iustin Pop
  @type ip: str
699 58885d79 Iustin Pop
  @param ip: the address to be checked
700 58885d79 Iustin Pop
  @rtype: a regular expression match object
701 5bbd3f7f Michael Hanselmann
  @return: a regular expression match object, or None if the
702 58885d79 Iustin Pop
      address is not valid
703 a8083063 Iustin Pop

704 a8083063 Iustin Pop
  """
705 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
706 58885d79 Iustin Pop
  #TODO: convert and return only boolean
707 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
708 a8083063 Iustin Pop
709 a8083063 Iustin Pop
710 a8083063 Iustin Pop
def IsValidShellParam(word):
711 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
712 a8083063 Iustin Pop

713 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
714 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
715 a8083063 Iustin Pop
  the actual command.
716 a8083063 Iustin Pop

717 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
718 a8083063 Iustin Pop
  side.
719 a8083063 Iustin Pop

720 58885d79 Iustin Pop
  @type word: str
721 58885d79 Iustin Pop
  @param word: the word to check
722 58885d79 Iustin Pop
  @rtype: boolean
723 58885d79 Iustin Pop
  @return: True if the word is 'safe'
724 58885d79 Iustin Pop

725 a8083063 Iustin Pop
  """
726 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
727 a8083063 Iustin Pop
728 a8083063 Iustin Pop
729 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
730 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
731 a8083063 Iustin Pop

732 a8083063 Iustin Pop
  This function will check all arguments in the args list so that they
733 a8083063 Iustin Pop
  are valid shell parameters (i.e. they don't contain shell
734 5bbd3f7f Michael Hanselmann
  metacharacters). If everything is ok, it will return the result of
735 a8083063 Iustin Pop
  template % args.
736 a8083063 Iustin Pop

737 58885d79 Iustin Pop
  @type template: str
738 58885d79 Iustin Pop
  @param template: the string holding the template for the
739 58885d79 Iustin Pop
      string formatting
740 58885d79 Iustin Pop
  @rtype: str
741 58885d79 Iustin Pop
  @return: the expanded command line
742 58885d79 Iustin Pop

743 a8083063 Iustin Pop
  """
744 a8083063 Iustin Pop
  for word in args:
745 a8083063 Iustin Pop
    if not IsValidShellParam(word):
746 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
747 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
748 a8083063 Iustin Pop
  return template % args
749 a8083063 Iustin Pop
750 a8083063 Iustin Pop
751 9fbfbb7b Iustin Pop
def FormatUnit(value, units):
752 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
753 a8083063 Iustin Pop

754 58885d79 Iustin Pop
  @type value: int
755 58885d79 Iustin Pop
  @param value: integer representing the value in MiB (1048576)
756 9fbfbb7b Iustin Pop
  @type units: char
757 9fbfbb7b Iustin Pop
  @param units: the type of formatting we should do:
758 9fbfbb7b Iustin Pop
      - 'h' for automatic scaling
759 9fbfbb7b Iustin Pop
      - 'm' for MiBs
760 9fbfbb7b Iustin Pop
      - 'g' for GiBs
761 9fbfbb7b Iustin Pop
      - 't' for TiBs
762 58885d79 Iustin Pop
  @rtype: str
763 58885d79 Iustin Pop
  @return: the formatted value (with suffix)
764 a8083063 Iustin Pop

765 a8083063 Iustin Pop
  """
766 9fbfbb7b Iustin Pop
  if units not in ('m', 'g', 't', 'h'):
767 9fbfbb7b Iustin Pop
    raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
768 a8083063 Iustin Pop
769 9fbfbb7b Iustin Pop
  suffix = ''
770 9fbfbb7b Iustin Pop
771 9fbfbb7b Iustin Pop
  if units == 'm' or (units == 'h' and value < 1024):
772 9fbfbb7b Iustin Pop
    if units == 'h':
773 9fbfbb7b Iustin Pop
      suffix = 'M'
774 9fbfbb7b Iustin Pop
    return "%d%s" % (round(value, 0), suffix)
775 9fbfbb7b Iustin Pop
776 9fbfbb7b Iustin Pop
  elif units == 'g' or (units == 'h' and value < (1024 * 1024)):
777 9fbfbb7b Iustin Pop
    if units == 'h':
778 9fbfbb7b Iustin Pop
      suffix = 'G'
779 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024, 1), suffix)
780 a8083063 Iustin Pop
781 a8083063 Iustin Pop
  else:
782 9fbfbb7b Iustin Pop
    if units == 'h':
783 9fbfbb7b Iustin Pop
      suffix = 'T'
784 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
785 a8083063 Iustin Pop
786 a8083063 Iustin Pop
787 a8083063 Iustin Pop
def ParseUnit(input_string):
788 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
789 a8083063 Iustin Pop

790 58885d79 Iustin Pop
  Input must be in the format C{NUMBER+ [DOT NUMBER+] SPACE*
791 58885d79 Iustin Pop
  [UNIT]}. If no unit is specified, it defaults to MiB. Return value
792 58885d79 Iustin Pop
  is always an int in MiB.
793 a8083063 Iustin Pop

794 a8083063 Iustin Pop
  """
795 9939547b Iustin Pop
  m = re.match('^([.\d]+)\s*([a-zA-Z]+)?$', str(input_string))
796 a8083063 Iustin Pop
  if not m:
797 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Invalid format")
798 a8083063 Iustin Pop
799 a8083063 Iustin Pop
  value = float(m.groups()[0])
800 a8083063 Iustin Pop
801 a8083063 Iustin Pop
  unit = m.groups()[1]
802 a8083063 Iustin Pop
  if unit:
803 a8083063 Iustin Pop
    lcunit = unit.lower()
804 a8083063 Iustin Pop
  else:
805 a8083063 Iustin Pop
    lcunit = 'm'
806 a8083063 Iustin Pop
807 a8083063 Iustin Pop
  if lcunit in ('m', 'mb', 'mib'):
808 a8083063 Iustin Pop
    # Value already in MiB
809 a8083063 Iustin Pop
    pass
810 a8083063 Iustin Pop
811 a8083063 Iustin Pop
  elif lcunit in ('g', 'gb', 'gib'):
812 a8083063 Iustin Pop
    value *= 1024
813 a8083063 Iustin Pop
814 a8083063 Iustin Pop
  elif lcunit in ('t', 'tb', 'tib'):
815 a8083063 Iustin Pop
    value *= 1024 * 1024
816 a8083063 Iustin Pop
817 a8083063 Iustin Pop
  else:
818 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Unknown unit: %s" % unit)
819 a8083063 Iustin Pop
820 a8083063 Iustin Pop
  # Make sure we round up
821 a8083063 Iustin Pop
  if int(value) < value:
822 a8083063 Iustin Pop
    value += 1
823 a8083063 Iustin Pop
824 a8083063 Iustin Pop
  # Round up to the next multiple of 4
825 a8083063 Iustin Pop
  value = int(value)
826 a8083063 Iustin Pop
  if value % 4:
827 a8083063 Iustin Pop
    value += 4 - value % 4
828 a8083063 Iustin Pop
829 a8083063 Iustin Pop
  return value
830 a8083063 Iustin Pop
831 a8083063 Iustin Pop
832 a8083063 Iustin Pop
def AddAuthorizedKey(file_name, key):
833 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
834 a8083063 Iustin Pop

835 58885d79 Iustin Pop
  @type file_name: str
836 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
837 58885d79 Iustin Pop
  @type key: str
838 58885d79 Iustin Pop
  @param key: string containing key
839 58885d79 Iustin Pop

840 a8083063 Iustin Pop
  """
841 a8083063 Iustin Pop
  key_fields = key.split()
842 a8083063 Iustin Pop
843 a8083063 Iustin Pop
  f = open(file_name, 'a+')
844 a8083063 Iustin Pop
  try:
845 a8083063 Iustin Pop
    nl = True
846 a8083063 Iustin Pop
    for line in f:
847 a8083063 Iustin Pop
      # Ignore whitespace changes
848 a8083063 Iustin Pop
      if line.split() == key_fields:
849 a8083063 Iustin Pop
        break
850 a8083063 Iustin Pop
      nl = line.endswith('\n')
851 a8083063 Iustin Pop
    else:
852 a8083063 Iustin Pop
      if not nl:
853 a8083063 Iustin Pop
        f.write("\n")
854 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
855 a8083063 Iustin Pop
      f.write("\n")
856 a8083063 Iustin Pop
      f.flush()
857 a8083063 Iustin Pop
  finally:
858 a8083063 Iustin Pop
    f.close()
859 a8083063 Iustin Pop
860 a8083063 Iustin Pop
861 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
862 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
863 a8083063 Iustin Pop

864 58885d79 Iustin Pop
  @type file_name: str
865 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
866 58885d79 Iustin Pop
  @type key: str
867 58885d79 Iustin Pop
  @param key: string containing key
868 58885d79 Iustin Pop

869 a8083063 Iustin Pop
  """
870 a8083063 Iustin Pop
  key_fields = key.split()
871 a8083063 Iustin Pop
872 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
873 a8083063 Iustin Pop
  try:
874 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
875 a8083063 Iustin Pop
    try:
876 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
877 59f82e3f Michael Hanselmann
      try:
878 59f82e3f Michael Hanselmann
        for line in f:
879 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
880 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
881 59f82e3f Michael Hanselmann
            out.write(line)
882 899d2a81 Michael Hanselmann
883 899d2a81 Michael Hanselmann
        out.flush()
884 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
885 899d2a81 Michael Hanselmann
      finally:
886 899d2a81 Michael Hanselmann
        f.close()
887 899d2a81 Michael Hanselmann
    finally:
888 899d2a81 Michael Hanselmann
      out.close()
889 899d2a81 Michael Hanselmann
  except:
890 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
891 899d2a81 Michael Hanselmann
    raise
892 899d2a81 Michael Hanselmann
893 899d2a81 Michael Hanselmann
894 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
895 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
896 899d2a81 Michael Hanselmann

897 58885d79 Iustin Pop
  @type file_name: str
898 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
899 58885d79 Iustin Pop
  @type ip: str
900 58885d79 Iustin Pop
  @param ip: the IP address
901 58885d79 Iustin Pop
  @type hostname: str
902 58885d79 Iustin Pop
  @param hostname: the hostname to be added
903 58885d79 Iustin Pop
  @type aliases: list
904 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
905 58885d79 Iustin Pop

906 899d2a81 Michael Hanselmann
  """
907 9b977740 Guido Trotter
  # FIXME: use WriteFile + fn rather than duplicating its efforts
908 7fbb1f65 Michael Hanselmann
  # Ensure aliases are unique
909 7fbb1f65 Michael Hanselmann
  aliases = UniqueSequence([hostname] + aliases)[1:]
910 7fbb1f65 Michael Hanselmann
911 9440aeab Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
912 899d2a81 Michael Hanselmann
  try:
913 9440aeab Michael Hanselmann
    out = os.fdopen(fd, 'w')
914 9440aeab Michael Hanselmann
    try:
915 9440aeab Michael Hanselmann
      f = open(file_name, 'r')
916 9440aeab Michael Hanselmann
      try:
917 9440aeab Michael Hanselmann
        for line in f:
918 9440aeab Michael Hanselmann
          fields = line.split()
919 7e3dbb94 Iustin Pop
          if fields and not fields[0].startswith('#') and ip == fields[0]:
920 9440aeab Michael Hanselmann
            continue
921 9440aeab Michael Hanselmann
          out.write(line)
922 9440aeab Michael Hanselmann
923 7e3dbb94 Iustin Pop
        out.write("%s\t%s" % (ip, hostname))
924 9440aeab Michael Hanselmann
        if aliases:
925 9440aeab Michael Hanselmann
          out.write(" %s" % ' '.join(aliases))
926 9440aeab Michael Hanselmann
        out.write('\n')
927 9440aeab Michael Hanselmann
928 9440aeab Michael Hanselmann
        out.flush()
929 2e3e75b7 Michael Hanselmann
        os.fsync(out)
930 9b977740 Guido Trotter
        os.chmod(tmpname, 0644)
931 9440aeab Michael Hanselmann
        os.rename(tmpname, file_name)
932 9440aeab Michael Hanselmann
      finally:
933 9440aeab Michael Hanselmann
        f.close()
934 9440aeab Michael Hanselmann
    finally:
935 9440aeab Michael Hanselmann
      out.close()
936 9440aeab Michael Hanselmann
  except:
937 9440aeab Michael Hanselmann
    RemoveFile(tmpname)
938 9440aeab Michael Hanselmann
    raise
939 899d2a81 Michael Hanselmann
940 899d2a81 Michael Hanselmann
941 d9c02ca6 Michael Hanselmann
def AddHostToEtcHosts(hostname):
942 d9c02ca6 Michael Hanselmann
  """Wrapper around SetEtcHostsEntry.
943 d9c02ca6 Michael Hanselmann

944 58885d79 Iustin Pop
  @type hostname: str
945 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
946 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
947 58885d79 Iustin Pop

948 d9c02ca6 Michael Hanselmann
  """
949 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
950 d9c02ca6 Michael Hanselmann
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
951 d9c02ca6 Michael Hanselmann
952 d9c02ca6 Michael Hanselmann
953 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
954 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
955 899d2a81 Michael Hanselmann

956 9440aeab Michael Hanselmann
  IP addresses without names are removed from the file.
957 58885d79 Iustin Pop

958 58885d79 Iustin Pop
  @type file_name: str
959 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
960 58885d79 Iustin Pop
  @type hostname: str
961 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
962 58885d79 Iustin Pop

963 899d2a81 Michael Hanselmann
  """
964 9b977740 Guido Trotter
  # FIXME: use WriteFile + fn rather than duplicating its efforts
965 899d2a81 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
966 899d2a81 Michael Hanselmann
  try:
967 899d2a81 Michael Hanselmann
    out = os.fdopen(fd, 'w')
968 899d2a81 Michael Hanselmann
    try:
969 899d2a81 Michael Hanselmann
      f = open(file_name, 'r')
970 899d2a81 Michael Hanselmann
      try:
971 899d2a81 Michael Hanselmann
        for line in f:
972 899d2a81 Michael Hanselmann
          fields = line.split()
973 899d2a81 Michael Hanselmann
          if len(fields) > 1 and not fields[0].startswith('#'):
974 899d2a81 Michael Hanselmann
            names = fields[1:]
975 899d2a81 Michael Hanselmann
            if hostname in names:
976 899d2a81 Michael Hanselmann
              while hostname in names:
977 899d2a81 Michael Hanselmann
                names.remove(hostname)
978 899d2a81 Michael Hanselmann
              if names:
979 9440aeab Michael Hanselmann
                out.write("%s %s\n" % (fields[0], ' '.join(names)))
980 899d2a81 Michael Hanselmann
              continue
981 899d2a81 Michael Hanselmann
982 899d2a81 Michael Hanselmann
          out.write(line)
983 59f82e3f Michael Hanselmann
984 59f82e3f Michael Hanselmann
        out.flush()
985 2e3e75b7 Michael Hanselmann
        os.fsync(out)
986 9b977740 Guido Trotter
        os.chmod(tmpname, 0644)
987 59f82e3f Michael Hanselmann
        os.rename(tmpname, file_name)
988 59f82e3f Michael Hanselmann
      finally:
989 59f82e3f Michael Hanselmann
        f.close()
990 a8083063 Iustin Pop
    finally:
991 59f82e3f Michael Hanselmann
      out.close()
992 59f82e3f Michael Hanselmann
  except:
993 59f82e3f Michael Hanselmann
    RemoveFile(tmpname)
994 59f82e3f Michael Hanselmann
    raise
995 a8083063 Iustin Pop
996 a8083063 Iustin Pop
997 d9c02ca6 Michael Hanselmann
def RemoveHostFromEtcHosts(hostname):
998 d9c02ca6 Michael Hanselmann
  """Wrapper around RemoveEtcHostsEntry.
999 d9c02ca6 Michael Hanselmann

1000 58885d79 Iustin Pop
  @type hostname: str
1001 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
1002 58885d79 Iustin Pop
      full and shot name will be removed from
1003 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1004 58885d79 Iustin Pop

1005 d9c02ca6 Michael Hanselmann
  """
1006 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
1007 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
1008 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
1009 d9c02ca6 Michael Hanselmann
1010 d9c02ca6 Michael Hanselmann
1011 a8083063 Iustin Pop
def CreateBackup(file_name):
1012 a8083063 Iustin Pop
  """Creates a backup of a file.
1013 a8083063 Iustin Pop

1014 58885d79 Iustin Pop
  @type file_name: str
1015 58885d79 Iustin Pop
  @param file_name: file to be backed up
1016 58885d79 Iustin Pop
  @rtype: str
1017 58885d79 Iustin Pop
  @return: the path to the newly created backup
1018 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
1019 a8083063 Iustin Pop

1020 a8083063 Iustin Pop
  """
1021 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
1022 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
1023 3ecf6786 Iustin Pop
                                file_name)
1024 a8083063 Iustin Pop
1025 081b1e69 Michael Hanselmann
  prefix = '%s.backup-%d.' % (os.path.basename(file_name), int(time.time()))
1026 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
1027 081b1e69 Michael Hanselmann
1028 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
1029 081b1e69 Michael Hanselmann
  try:
1030 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
1031 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
1032 081b1e69 Michael Hanselmann
    try:
1033 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
1034 081b1e69 Michael Hanselmann
    finally:
1035 081b1e69 Michael Hanselmann
      fdst.close()
1036 081b1e69 Michael Hanselmann
  finally:
1037 081b1e69 Michael Hanselmann
    fsrc.close()
1038 081b1e69 Michael Hanselmann
1039 a8083063 Iustin Pop
  return backup_name
1040 a8083063 Iustin Pop
1041 a8083063 Iustin Pop
1042 a8083063 Iustin Pop
def ShellQuote(value):
1043 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
1044 3ecf6786 Iustin Pop

1045 58885d79 Iustin Pop
  @type value: str
1046 58885d79 Iustin Pop
  @param value: the argument to be quoted
1047 58885d79 Iustin Pop
  @rtype: str
1048 58885d79 Iustin Pop
  @return: the quoted value
1049 58885d79 Iustin Pop

1050 a8083063 Iustin Pop
  """
1051 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
1052 a8083063 Iustin Pop
    return value
1053 a8083063 Iustin Pop
  else:
1054 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
1055 a8083063 Iustin Pop
1056 a8083063 Iustin Pop
1057 a8083063 Iustin Pop
def ShellQuoteArgs(args):
1058 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
1059 58885d79 Iustin Pop

1060 58885d79 Iustin Pop
  @type args: list
1061 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1062 58885d79 Iustin Pop
  @rtype: str
1063 5bbd3f7f Michael Hanselmann
  @return: the quoted arguments concatenated with spaces
1064 a8083063 Iustin Pop

1065 a8083063 Iustin Pop
  """
1066 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
1067 88d14415 Michael Hanselmann
1068 88d14415 Michael Hanselmann
1069 b15d625f Iustin Pop
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
1070 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
1071 2c30e9d7 Alexander Schreiber

1072 58885d79 Iustin Pop
  Check if the given IP is reachable by doing attempting a TCP connect
1073 58885d79 Iustin Pop
  to it.
1074 58885d79 Iustin Pop

1075 58885d79 Iustin Pop
  @type target: str
1076 58885d79 Iustin Pop
  @param target: the IP or hostname to ping
1077 58885d79 Iustin Pop
  @type port: int
1078 58885d79 Iustin Pop
  @param port: the port to connect to
1079 58885d79 Iustin Pop
  @type timeout: int
1080 5bbd3f7f Michael Hanselmann
  @param timeout: the timeout on the connection attempt
1081 58885d79 Iustin Pop
  @type live_port_needed: boolean
1082 58885d79 Iustin Pop
  @param live_port_needed: whether a closed port will cause the
1083 58885d79 Iustin Pop
      function to return failure, as if there was a timeout
1084 58885d79 Iustin Pop
  @type source: str or None
1085 58885d79 Iustin Pop
  @param source: if specified, will cause the connect to be made
1086 58885d79 Iustin Pop
      from this specific source address; failures to bind other
1087 58885d79 Iustin Pop
      than C{EADDRNOTAVAIL} will be ignored
1088 2c30e9d7 Alexander Schreiber

1089 2c30e9d7 Alexander Schreiber
  """
1090 2c30e9d7 Alexander Schreiber
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1091 2c30e9d7 Alexander Schreiber
1092 0b5ad33e Iustin Pop
  success = False
1093 2c30e9d7 Alexander Schreiber
1094 b15d625f Iustin Pop
  if source is not None:
1095 b15d625f Iustin Pop
    try:
1096 b15d625f Iustin Pop
      sock.bind((source, 0))
1097 7c4d6c7b Michael Hanselmann
    except socket.error, (errcode, _):
1098 b15d625f Iustin Pop
      if errcode == errno.EADDRNOTAVAIL:
1099 b15d625f Iustin Pop
        success = False
1100 2c30e9d7 Alexander Schreiber
1101 2c30e9d7 Alexander Schreiber
  sock.settimeout(timeout)
1102 2c30e9d7 Alexander Schreiber
1103 2c30e9d7 Alexander Schreiber
  try:
1104 2c30e9d7 Alexander Schreiber
    sock.connect((target, port))
1105 2c30e9d7 Alexander Schreiber
    sock.close()
1106 2c30e9d7 Alexander Schreiber
    success = True
1107 2c30e9d7 Alexander Schreiber
  except socket.timeout:
1108 2c30e9d7 Alexander Schreiber
    success = False
1109 099c52ad Iustin Pop
  except socket.error, (errcode, _):
1110 4ca1b175 Alexander Schreiber
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
1111 2c30e9d7 Alexander Schreiber
1112 2c30e9d7 Alexander Schreiber
  return success
1113 eedbda4b Michael Hanselmann
1114 eedbda4b Michael Hanselmann
1115 caad16e2 Iustin Pop
def OwnIpAddress(address):
1116 caad16e2 Iustin Pop
  """Check if the current host has the the given IP address.
1117 caad16e2 Iustin Pop

1118 58885d79 Iustin Pop
  Currently this is done by TCP-pinging the address from the loopback
1119 caad16e2 Iustin Pop
  address.
1120 caad16e2 Iustin Pop

1121 caad16e2 Iustin Pop
  @type address: string
1122 5bbd3f7f Michael Hanselmann
  @param address: the address to check
1123 caad16e2 Iustin Pop
  @rtype: bool
1124 58885d79 Iustin Pop
  @return: True if we own the address
1125 caad16e2 Iustin Pop

1126 caad16e2 Iustin Pop
  """
1127 caad16e2 Iustin Pop
  return TcpPing(address, constants.DEFAULT_NODED_PORT,
1128 caad16e2 Iustin Pop
                 source=constants.LOCALHOST_IP_ADDRESS)
1129 caad16e2 Iustin Pop
1130 caad16e2 Iustin Pop
1131 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
1132 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1133 58885d79 Iustin Pop

1134 58885d79 Iustin Pop
  @type path: str
1135 58885d79 Iustin Pop
  @param path: the directory to enumerate
1136 58885d79 Iustin Pop
  @rtype: list
1137 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1138 eedbda4b Michael Hanselmann

1139 eedbda4b Michael Hanselmann
  """
1140 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1141 f3299a07 Michael Hanselmann
  files.sort()
1142 f3299a07 Michael Hanselmann
  return files
1143 2f8b60b3 Iustin Pop
1144 2f8b60b3 Iustin Pop
1145 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1146 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1147 257f4c0a Iustin Pop

1148 257f4c0a Iustin Pop
  The user can be passed either as a string (denoting the name) or as
1149 257f4c0a Iustin Pop
  an integer (denoting the user id). If the user is not found, the
1150 257f4c0a Iustin Pop
  'default' argument is returned, which defaults to None.
1151 2f8b60b3 Iustin Pop

1152 2f8b60b3 Iustin Pop
  """
1153 2f8b60b3 Iustin Pop
  try:
1154 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1155 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1156 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1157 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1158 257f4c0a Iustin Pop
    else:
1159 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1160 257f4c0a Iustin Pop
                                   type(user))
1161 2f8b60b3 Iustin Pop
  except KeyError:
1162 2f8b60b3 Iustin Pop
    return default
1163 2f8b60b3 Iustin Pop
  return result.pw_dir
1164 59072e7e Michael Hanselmann
1165 59072e7e Michael Hanselmann
1166 24818e8f Michael Hanselmann
def NewUUID():
1167 59072e7e Michael Hanselmann
  """Returns a random UUID.
1168 59072e7e Michael Hanselmann

1169 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1170 58885d79 Iustin Pop
      filesystem.
1171 58885d79 Iustin Pop
  @rtype: str
1172 58885d79 Iustin Pop

1173 59072e7e Michael Hanselmann
  """
1174 13998ef2 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1175 087b34fe Iustin Pop
1176 087b34fe Iustin Pop
1177 ec2c2bc4 Luca Bigliardi
def GenerateSecret(numbytes=20):
1178 33081d90 Iustin Pop
  """Generates a random secret.
1179 33081d90 Iustin Pop

1180 ec2c2bc4 Luca Bigliardi
  This will generate a pseudo-random secret returning an hex string
1181 33081d90 Iustin Pop
  (so that it can be used where an ASCII string is needed).
1182 33081d90 Iustin Pop

1183 ec2c2bc4 Luca Bigliardi
  @param numbytes: the number of bytes which will be represented by the returned
1184 ec2c2bc4 Luca Bigliardi
      string (defaulting to 20, the length of a SHA1 hash)
1185 58885d79 Iustin Pop
  @rtype: str
1186 ec2c2bc4 Luca Bigliardi
  @return: an hex representation of the pseudo-random sequence
1187 58885d79 Iustin Pop

1188 33081d90 Iustin Pop
  """
1189 ec2c2bc4 Luca Bigliardi
  return os.urandom(numbytes).encode('hex')
1190 33081d90 Iustin Pop
1191 33081d90 Iustin Pop
1192 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1193 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1194 9dae41ad Guido Trotter

1195 9dae41ad Guido Trotter
  @param dirs: list of tuples (dir_name, dir_mode)
1196 9dae41ad Guido Trotter
  @type dirs: list of (string, integer)
1197 9dae41ad Guido Trotter

1198 9dae41ad Guido Trotter
  """
1199 9dae41ad Guido Trotter
  for dir_name, dir_mode in dirs:
1200 9dae41ad Guido Trotter
    try:
1201 1b2c8f85 Iustin Pop
      os.mkdir(dir_name, dir_mode)
1202 9dae41ad Guido Trotter
    except EnvironmentError, err:
1203 9dae41ad Guido Trotter
      if err.errno != errno.EEXIST:
1204 9dae41ad Guido Trotter
        raise errors.GenericError("Cannot create needed directory"
1205 1b2c8f85 Iustin Pop
                                  " '%s': %s" % (dir_name, err))
1206 9dae41ad Guido Trotter
    if not os.path.isdir(dir_name):
1207 9dae41ad Guido Trotter
      raise errors.GenericError("%s is not a directory" % dir_name)
1208 9dae41ad Guido Trotter
1209 9dae41ad Guido Trotter
1210 ca0aa6d0 Michael Hanselmann
def ReadFile(file_name, size=None):
1211 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1212 ca0aa6d0 Michael Hanselmann

1213 ca0aa6d0 Michael Hanselmann
  @type size: None or int
1214 ca0aa6d0 Michael Hanselmann
  @param size: Read at most size bytes
1215 58885d79 Iustin Pop
  @rtype: str
1216 5bbd3f7f Michael Hanselmann
  @return: the (possibly partial) content of the file
1217 ca0aa6d0 Michael Hanselmann

1218 ca0aa6d0 Michael Hanselmann
  """
1219 ca0aa6d0 Michael Hanselmann
  f = open(file_name, "r")
1220 ca0aa6d0 Michael Hanselmann
  try:
1221 ca0aa6d0 Michael Hanselmann
    if size is None:
1222 ca0aa6d0 Michael Hanselmann
      return f.read()
1223 ca0aa6d0 Michael Hanselmann
    else:
1224 ca0aa6d0 Michael Hanselmann
      return f.read(size)
1225 ca0aa6d0 Michael Hanselmann
  finally:
1226 ca0aa6d0 Michael Hanselmann
    f.close()
1227 ca0aa6d0 Michael Hanselmann
1228 ca0aa6d0 Michael Hanselmann
1229 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
1230 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
1231 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
1232 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
1233 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
1234 087b34fe Iustin Pop
  """(Over)write a file atomically.
1235 087b34fe Iustin Pop

1236 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
1237 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
1238 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
1239 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
1240 087b34fe Iustin Pop
  mtime/atime of the file.
1241 087b34fe Iustin Pop

1242 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1243 69efe319 Michael Hanselmann
  target file has the new contents. If the function has raised an
1244 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1245 087b34fe Iustin Pop
  temporary file should be removed.
1246 087b34fe Iustin Pop

1247 58885d79 Iustin Pop
  @type file_name: str
1248 58885d79 Iustin Pop
  @param file_name: the target filename
1249 58885d79 Iustin Pop
  @type fn: callable
1250 58885d79 Iustin Pop
  @param fn: content writing function, called with
1251 58885d79 Iustin Pop
      file descriptor as parameter
1252 69efe319 Michael Hanselmann
  @type data: str
1253 58885d79 Iustin Pop
  @param data: contents of the file
1254 58885d79 Iustin Pop
  @type mode: int
1255 58885d79 Iustin Pop
  @param mode: file mode
1256 58885d79 Iustin Pop
  @type uid: int
1257 58885d79 Iustin Pop
  @param uid: the owner of the file
1258 58885d79 Iustin Pop
  @type gid: int
1259 58885d79 Iustin Pop
  @param gid: the group of the file
1260 58885d79 Iustin Pop
  @type atime: int
1261 58885d79 Iustin Pop
  @param atime: a custom access time to be set on the file
1262 58885d79 Iustin Pop
  @type mtime: int
1263 58885d79 Iustin Pop
  @param mtime: a custom modification time to be set on the file
1264 58885d79 Iustin Pop
  @type close: boolean
1265 58885d79 Iustin Pop
  @param close: whether to close file after writing it
1266 58885d79 Iustin Pop
  @type prewrite: callable
1267 58885d79 Iustin Pop
  @param prewrite: function to be called before writing content
1268 58885d79 Iustin Pop
  @type postwrite: callable
1269 58885d79 Iustin Pop
  @param postwrite: function to be called after writing content
1270 58885d79 Iustin Pop

1271 58885d79 Iustin Pop
  @rtype: None or int
1272 58885d79 Iustin Pop
  @return: None if the 'close' parameter evaluates to True,
1273 58885d79 Iustin Pop
      otherwise the file descriptor
1274 58885d79 Iustin Pop

1275 69efe319 Michael Hanselmann
  @raise errors.ProgrammerError: if any of the arguments are not valid
1276 71714516 Michael Hanselmann

1277 087b34fe Iustin Pop
  """
1278 04a8d789 Michael Hanselmann
  if not os.path.isabs(file_name):
1279 087b34fe Iustin Pop
    raise errors.ProgrammerError("Path passed to WriteFile is not"
1280 087b34fe Iustin Pop
                                 " absolute: '%s'" % file_name)
1281 087b34fe Iustin Pop
1282 087b34fe Iustin Pop
  if [fn, data].count(None) != 1:
1283 087b34fe Iustin Pop
    raise errors.ProgrammerError("fn or data required")
1284 087b34fe Iustin Pop
1285 087b34fe Iustin Pop
  if [atime, mtime].count(None) == 1:
1286 087b34fe Iustin Pop
    raise errors.ProgrammerError("Both atime and mtime must be either"
1287 087b34fe Iustin Pop
                                 " set or None")
1288 087b34fe Iustin Pop
1289 70f4497c Michael Hanselmann
  if backup and not dry_run and os.path.isfile(file_name):
1290 70f4497c Michael Hanselmann
    CreateBackup(file_name)
1291 087b34fe Iustin Pop
1292 087b34fe Iustin Pop
  dir_name, base_name = os.path.split(file_name)
1293 087b34fe Iustin Pop
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
1294 81b7354c Iustin Pop
  do_remove = True
1295 087b34fe Iustin Pop
  # here we need to make sure we remove the temp file, if any error
1296 087b34fe Iustin Pop
  # leaves it in place
1297 087b34fe Iustin Pop
  try:
1298 087b34fe Iustin Pop
    if uid != -1 or gid != -1:
1299 087b34fe Iustin Pop
      os.chown(new_name, uid, gid)
1300 087b34fe Iustin Pop
    if mode:
1301 087b34fe Iustin Pop
      os.chmod(new_name, mode)
1302 71714516 Michael Hanselmann
    if callable(prewrite):
1303 71714516 Michael Hanselmann
      prewrite(fd)
1304 087b34fe Iustin Pop
    if data is not None:
1305 087b34fe Iustin Pop
      os.write(fd, data)
1306 087b34fe Iustin Pop
    else:
1307 087b34fe Iustin Pop
      fn(fd)
1308 71714516 Michael Hanselmann
    if callable(postwrite):
1309 71714516 Michael Hanselmann
      postwrite(fd)
1310 087b34fe Iustin Pop
    os.fsync(fd)
1311 087b34fe Iustin Pop
    if atime is not None and mtime is not None:
1312 087b34fe Iustin Pop
      os.utime(new_name, (atime, mtime))
1313 70f4497c Michael Hanselmann
    if not dry_run:
1314 70f4497c Michael Hanselmann
      os.rename(new_name, file_name)
1315 81b7354c Iustin Pop
      do_remove = False
1316 087b34fe Iustin Pop
  finally:
1317 71714516 Michael Hanselmann
    if close:
1318 71714516 Michael Hanselmann
      os.close(fd)
1319 71714516 Michael Hanselmann
      result = None
1320 71714516 Michael Hanselmann
    else:
1321 71714516 Michael Hanselmann
      result = fd
1322 81b7354c Iustin Pop
    if do_remove:
1323 81b7354c Iustin Pop
      RemoveFile(new_name)
1324 78feb6fb Guido Trotter
1325 71714516 Michael Hanselmann
  return result
1326 71714516 Michael Hanselmann
1327 78feb6fb Guido Trotter
1328 7b4126b7 Iustin Pop
def FirstFree(seq, base=0):
1329 7b4126b7 Iustin Pop
  """Returns the first non-existing integer from seq.
1330 7b4126b7 Iustin Pop

1331 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
1332 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
1333 7b4126b7 Iustin Pop
  value, the index will be returned.
1334 7b4126b7 Iustin Pop

1335 7b4126b7 Iustin Pop
  The base argument is used to start at a different offset,
1336 58885d79 Iustin Pop
  i.e. C{[3, 4, 6]} with I{offset=3} will return 5.
1337 58885d79 Iustin Pop

1338 58885d79 Iustin Pop
  Example: C{[0, 1, 3]} will return I{2}.
1339 7b4126b7 Iustin Pop

1340 58885d79 Iustin Pop
  @type seq: sequence
1341 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
1342 58885d79 Iustin Pop
  @type base: int
1343 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
1344 58885d79 Iustin Pop
  @rtype: int
1345 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
1346 7b4126b7 Iustin Pop

1347 7b4126b7 Iustin Pop
  """
1348 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
1349 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
1350 7b4126b7 Iustin Pop
    if elem > idx + base:
1351 7b4126b7 Iustin Pop
      # idx is not used
1352 7b4126b7 Iustin Pop
      return idx + base
1353 7b4126b7 Iustin Pop
  return None
1354 7b4126b7 Iustin Pop
1355 7b4126b7 Iustin Pop
1356 78feb6fb Guido Trotter
def all(seq, pred=bool):
1357 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for every element in the iterable"
1358 7c4d6c7b Michael Hanselmann
  for _ in itertools.ifilterfalse(pred, seq):
1359 78feb6fb Guido Trotter
    return False
1360 78feb6fb Guido Trotter
  return True
1361 78feb6fb Guido Trotter
1362 78feb6fb Guido Trotter
1363 78feb6fb Guido Trotter
def any(seq, pred=bool):
1364 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for at least one element in the iterable"
1365 7c4d6c7b Michael Hanselmann
  for _ in itertools.ifilter(pred, seq):
1366 78feb6fb Guido Trotter
    return True
1367 78feb6fb Guido Trotter
  return False
1368 f7414041 Michael Hanselmann
1369 f7414041 Michael Hanselmann
1370 f7414041 Michael Hanselmann
def UniqueSequence(seq):
1371 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
1372 f7414041 Michael Hanselmann

1373 f7414041 Michael Hanselmann
  Element order is preserved.
1374 58885d79 Iustin Pop

1375 58885d79 Iustin Pop
  @type seq: sequence
1376 5bbd3f7f Michael Hanselmann
  @param seq: the sequence with the source elements
1377 58885d79 Iustin Pop
  @rtype: list
1378 58885d79 Iustin Pop
  @return: list of unique elements from seq
1379 58885d79 Iustin Pop

1380 f7414041 Michael Hanselmann
  """
1381 f7414041 Michael Hanselmann
  seen = set()
1382 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
1383 1862d460 Alexander Schreiber
1384 1862d460 Alexander Schreiber
1385 1862d460 Alexander Schreiber
def IsValidMac(mac):
1386 1862d460 Alexander Schreiber
  """Predicate to check if a MAC address is valid.
1387 1862d460 Alexander Schreiber

1388 5bbd3f7f Michael Hanselmann
  Checks whether the supplied MAC address is formally correct, only
1389 1862d460 Alexander Schreiber
  accepts colon separated format.
1390 58885d79 Iustin Pop

1391 58885d79 Iustin Pop
  @type mac: str
1392 58885d79 Iustin Pop
  @param mac: the MAC to be validated
1393 58885d79 Iustin Pop
  @rtype: boolean
1394 58885d79 Iustin Pop
  @return: True is the MAC seems valid
1395 58885d79 Iustin Pop

1396 1862d460 Alexander Schreiber
  """
1397 1862d460 Alexander Schreiber
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$")
1398 1862d460 Alexander Schreiber
  return mac_check.match(mac) is not None
1399 06009e27 Iustin Pop
1400 06009e27 Iustin Pop
1401 06009e27 Iustin Pop
def TestDelay(duration):
1402 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
1403 06009e27 Iustin Pop

1404 58885d79 Iustin Pop
  @type duration: float
1405 58885d79 Iustin Pop
  @param duration: the sleep duration
1406 58885d79 Iustin Pop
  @rtype: boolean
1407 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
1408 58885d79 Iustin Pop

1409 06009e27 Iustin Pop
  """
1410 06009e27 Iustin Pop
  if duration < 0:
1411 38ea42a1 Iustin Pop
    return False, "Invalid sleep duration"
1412 06009e27 Iustin Pop
  time.sleep(duration)
1413 38ea42a1 Iustin Pop
  return True, None
1414 8f765069 Iustin Pop
1415 8f765069 Iustin Pop
1416 7d88772a Iustin Pop
def _CloseFDNoErr(fd, retries=5):
1417 7d88772a Iustin Pop
  """Close a file descriptor ignoring errors.
1418 8f765069 Iustin Pop

1419 7d88772a Iustin Pop
  @type fd: int
1420 7d88772a Iustin Pop
  @param fd: the file descriptor
1421 7d88772a Iustin Pop
  @type retries: int
1422 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
1423 7d88772a Iustin Pop
      other error than EBADF
1424 7d88772a Iustin Pop

1425 7d88772a Iustin Pop
  """
1426 7d88772a Iustin Pop
  try:
1427 7d88772a Iustin Pop
    os.close(fd)
1428 7d88772a Iustin Pop
  except OSError, err:
1429 7d88772a Iustin Pop
    if err.errno != errno.EBADF:
1430 7d88772a Iustin Pop
      if retries > 0:
1431 7d88772a Iustin Pop
        _CloseFDNoErr(fd, retries - 1)
1432 7d88772a Iustin Pop
    # else either it's closed already or we're out of retries, so we
1433 7d88772a Iustin Pop
    # ignore this and go on
1434 7d88772a Iustin Pop
1435 7d88772a Iustin Pop
1436 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
1437 7d88772a Iustin Pop
  """Close file descriptors.
1438 7d88772a Iustin Pop

1439 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
1440 7d88772a Iustin Pop
  stdin/out/err).
1441 8f765069 Iustin Pop

1442 58885d79 Iustin Pop
  @type noclose_fds: list or None
1443 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
1444 58885d79 Iustin Pop
      that should not be closed
1445 58885d79 Iustin Pop

1446 8f765069 Iustin Pop
  """
1447 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
1448 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
1449 8f765069 Iustin Pop
    try:
1450 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
1451 8f765069 Iustin Pop
      if MAXFD < 0:
1452 8f765069 Iustin Pop
        MAXFD = 1024
1453 8f765069 Iustin Pop
    except OSError:
1454 8f765069 Iustin Pop
      MAXFD = 1024
1455 8f765069 Iustin Pop
  else:
1456 8f765069 Iustin Pop
    MAXFD = 1024
1457 7d88772a Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1458 7d88772a Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
1459 7d88772a Iustin Pop
    maxfd = MAXFD
1460 7d88772a Iustin Pop
1461 7d88772a Iustin Pop
  # Iterate through and close all file descriptors (except the standard ones)
1462 7d88772a Iustin Pop
  for fd in range(3, maxfd):
1463 7d88772a Iustin Pop
    if noclose_fds and fd in noclose_fds:
1464 7d88772a Iustin Pop
      continue
1465 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1466 7d88772a Iustin Pop
1467 7d88772a Iustin Pop
1468 7d88772a Iustin Pop
def Daemonize(logfile):
1469 7d88772a Iustin Pop
  """Daemonize the current process.
1470 7d88772a Iustin Pop

1471 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
1472 7d88772a Iustin Pop
  runs it in the background as a daemon.
1473 7d88772a Iustin Pop

1474 7d88772a Iustin Pop
  @type logfile: str
1475 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
1476 7d88772a Iustin Pop
  @rtype: int
1477 5fcc718f Iustin Pop
  @return: the value zero
1478 7d88772a Iustin Pop

1479 7d88772a Iustin Pop
  """
1480 7d88772a Iustin Pop
  UMASK = 077
1481 7d88772a Iustin Pop
  WORKDIR = "/"
1482 8f765069 Iustin Pop
1483 8f765069 Iustin Pop
  # this might fail
1484 8f765069 Iustin Pop
  pid = os.fork()
1485 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
1486 8f765069 Iustin Pop
    os.setsid()
1487 8f765069 Iustin Pop
    # this might fail
1488 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
1489 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
1490 8f765069 Iustin Pop
      os.chdir(WORKDIR)
1491 8f765069 Iustin Pop
      os.umask(UMASK)
1492 8f765069 Iustin Pop
    else:
1493 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
1494 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
1495 8f765069 Iustin Pop
  else:
1496 8f765069 Iustin Pop
    os._exit(0) # Exit parent of the first child.
1497 8f765069 Iustin Pop
1498 7d88772a Iustin Pop
  for fd in range(3):
1499 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1500 7d88772a Iustin Pop
  i = os.open("/dev/null", os.O_RDONLY) # stdin
1501 7d88772a Iustin Pop
  assert i == 0, "Can't close/reopen stdin"
1502 7d88772a Iustin Pop
  i = os.open(logfile, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0600) # stdout
1503 7d88772a Iustin Pop
  assert i == 1, "Can't close/reopen stdout"
1504 7d88772a Iustin Pop
  # Duplicate standard output to standard error.
1505 7d88772a Iustin Pop
  os.dup2(1, 2)
1506 8f765069 Iustin Pop
  return 0
1507 57c177af Iustin Pop
1508 57c177af Iustin Pop
1509 53beffbb Iustin Pop
def DaemonPidFileName(name):
1510 58885d79 Iustin Pop
  """Compute a ganeti pid file absolute path
1511 58885d79 Iustin Pop

1512 58885d79 Iustin Pop
  @type name: str
1513 58885d79 Iustin Pop
  @param name: the daemon name
1514 58885d79 Iustin Pop
  @rtype: str
1515 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
1516 58885d79 Iustin Pop
      daemon name
1517 b330ac0b Guido Trotter

1518 b330ac0b Guido Trotter
  """
1519 b330ac0b Guido Trotter
  return os.path.join(constants.RUN_GANETI_DIR, "%s.pid" % name)
1520 b330ac0b Guido Trotter
1521 b330ac0b Guido Trotter
1522 b330ac0b Guido Trotter
def WritePidFile(name):
1523 b330ac0b Guido Trotter
  """Write the current process pidfile.
1524 b330ac0b Guido Trotter

1525 58885d79 Iustin Pop
  The file will be written to L{constants.RUN_GANETI_DIR}I{/name.pid}
1526 58885d79 Iustin Pop

1527 58885d79 Iustin Pop
  @type name: str
1528 58885d79 Iustin Pop
  @param name: the daemon name to use
1529 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
1530 58885d79 Iustin Pop
      points to a live process
1531 b330ac0b Guido Trotter

1532 b330ac0b Guido Trotter
  """
1533 b330ac0b Guido Trotter
  pid = os.getpid()
1534 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1535 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
1536 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
1537 b330ac0b Guido Trotter
1538 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
1539 b330ac0b Guido Trotter
1540 b330ac0b Guido Trotter
1541 b330ac0b Guido Trotter
def RemovePidFile(name):
1542 b330ac0b Guido Trotter
  """Remove the current process pidfile.
1543 b330ac0b Guido Trotter

1544 b330ac0b Guido Trotter
  Any errors are ignored.
1545 b330ac0b Guido Trotter

1546 58885d79 Iustin Pop
  @type name: str
1547 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
1548 58885d79 Iustin Pop

1549 b330ac0b Guido Trotter
  """
1550 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1551 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
1552 b330ac0b Guido Trotter
  try:
1553 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
1554 b330ac0b Guido Trotter
  except:
1555 b330ac0b Guido Trotter
    pass
1556 b330ac0b Guido Trotter
1557 b330ac0b Guido Trotter
1558 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
1559 ff5251bc Iustin Pop
                waitpid=False):
1560 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
1561 b2a1f511 Iustin Pop

1562 b2a1f511 Iustin Pop
  @type pid: int
1563 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
1564 38206f3c Iustin Pop
  @type signal_: int
1565 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
1566 b2a1f511 Iustin Pop
  @type timeout: int
1567 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
1568 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
1569 b2a1f511 Iustin Pop
                  will be done
1570 ff5251bc Iustin Pop
  @type waitpid: boolean
1571 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
1572 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
1573 ff5251bc Iustin Pop
      would remain as zombie
1574 b2a1f511 Iustin Pop

1575 b2a1f511 Iustin Pop
  """
1576 ff5251bc Iustin Pop
  def _helper(pid, signal_, wait):
1577 ff5251bc Iustin Pop
    """Simple helper to encapsulate the kill/waitpid sequence"""
1578 ff5251bc Iustin Pop
    os.kill(pid, signal_)
1579 ff5251bc Iustin Pop
    if wait:
1580 ff5251bc Iustin Pop
      try:
1581 ff5251bc Iustin Pop
        os.waitpid(pid, os.WNOHANG)
1582 ff5251bc Iustin Pop
      except OSError:
1583 ff5251bc Iustin Pop
        pass
1584 ff5251bc Iustin Pop
1585 b2a1f511 Iustin Pop
  if pid <= 0:
1586 b2a1f511 Iustin Pop
    # kill with pid=0 == suicide
1587 b2a1f511 Iustin Pop
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
1588 b2a1f511 Iustin Pop
1589 b2a1f511 Iustin Pop
  if not IsProcessAlive(pid):
1590 b2a1f511 Iustin Pop
    return
1591 31892b4c Michael Hanselmann
1592 ff5251bc Iustin Pop
  _helper(pid, signal_, waitpid)
1593 31892b4c Michael Hanselmann
1594 b2a1f511 Iustin Pop
  if timeout <= 0:
1595 b2a1f511 Iustin Pop
    return
1596 7167159a Michael Hanselmann
1597 31892b4c Michael Hanselmann
  def _CheckProcess():
1598 31892b4c Michael Hanselmann
    if not IsProcessAlive(pid):
1599 31892b4c Michael Hanselmann
      return
1600 31892b4c Michael Hanselmann
1601 7167159a Michael Hanselmann
    try:
1602 7167159a Michael Hanselmann
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
1603 7167159a Michael Hanselmann
    except OSError:
1604 31892b4c Michael Hanselmann
      raise RetryAgain()
1605 31892b4c Michael Hanselmann
1606 31892b4c Michael Hanselmann
    if result_pid > 0:
1607 31892b4c Michael Hanselmann
      return
1608 31892b4c Michael Hanselmann
1609 31892b4c Michael Hanselmann
    raise RetryAgain()
1610 31892b4c Michael Hanselmann
1611 31892b4c Michael Hanselmann
  try:
1612 31892b4c Michael Hanselmann
    # Wait up to $timeout seconds
1613 31892b4c Michael Hanselmann
    Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
1614 31892b4c Michael Hanselmann
  except RetryTimeout:
1615 31892b4c Michael Hanselmann
    pass
1616 7167159a Michael Hanselmann
1617 b2a1f511 Iustin Pop
  if IsProcessAlive(pid):
1618 7167159a Michael Hanselmann
    # Kill process if it's still alive
1619 e1bd0072 Iustin Pop
    _helper(pid, signal.SIGKILL, waitpid)
1620 b2a1f511 Iustin Pop
1621 b2a1f511 Iustin Pop
1622 57c177af Iustin Pop
def FindFile(name, search_path, test=os.path.exists):
1623 57c177af Iustin Pop
  """Look for a filesystem object in a given path.
1624 57c177af Iustin Pop

1625 57c177af Iustin Pop
  This is an abstract method to search for filesystem object (files,
1626 57c177af Iustin Pop
  dirs) under a given search path.
1627 57c177af Iustin Pop

1628 58885d79 Iustin Pop
  @type name: str
1629 58885d79 Iustin Pop
  @param name: the name to look for
1630 58885d79 Iustin Pop
  @type search_path: str
1631 58885d79 Iustin Pop
  @param search_path: location to start at
1632 58885d79 Iustin Pop
  @type test: callable
1633 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
1634 58885d79 Iustin Pop
      if the a given object is valid; the default value is
1635 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
1636 58885d79 Iustin Pop
  @rtype: str or None
1637 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
1638 57c177af Iustin Pop

1639 57c177af Iustin Pop
  """
1640 57c177af Iustin Pop
  for dir_name in search_path:
1641 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
1642 57c177af Iustin Pop
    if test(item_name):
1643 57c177af Iustin Pop
      return item_name
1644 57c177af Iustin Pop
  return None
1645 8d1a2a64 Michael Hanselmann
1646 8d1a2a64 Michael Hanselmann
1647 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
1648 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
1649 8d1a2a64 Michael Hanselmann

1650 58885d79 Iustin Pop
  The function will check if a given volume group is in the list of
1651 58885d79 Iustin Pop
  volume groups and has a minimum size.
1652 58885d79 Iustin Pop

1653 58885d79 Iustin Pop
  @type vglist: dict
1654 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
1655 58885d79 Iustin Pop
  @type vgname: str
1656 58885d79 Iustin Pop
  @param vgname: the volume group we should check
1657 58885d79 Iustin Pop
  @type minsize: int
1658 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
1659 58885d79 Iustin Pop
  @rtype: None or str
1660 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
1661 8d1a2a64 Michael Hanselmann

1662 8d1a2a64 Michael Hanselmann
  """
1663 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
1664 8d1a2a64 Michael Hanselmann
  if vgsize is None:
1665 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
1666 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
1667 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
1668 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
1669 8d1a2a64 Michael Hanselmann
  return None
1670 7996a135 Iustin Pop
1671 7996a135 Iustin Pop
1672 45bc5e4a Michael Hanselmann
def SplitTime(value):
1673 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
1674 739be818 Michael Hanselmann

1675 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
1676 45bc5e4a Michael Hanselmann
  @type value: int or float
1677 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
1678 739be818 Michael Hanselmann

1679 739be818 Michael Hanselmann
  """
1680 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
1681 45bc5e4a Michael Hanselmann
1682 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1683 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1684 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1685 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1686 45bc5e4a Michael Hanselmann
1687 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
1688 739be818 Michael Hanselmann
1689 739be818 Michael Hanselmann
1690 739be818 Michael Hanselmann
def MergeTime(timetuple):
1691 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
1692 739be818 Michael Hanselmann

1693 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
1694 739be818 Michael Hanselmann
  @type timetuple: tuple
1695 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
1696 739be818 Michael Hanselmann

1697 739be818 Michael Hanselmann
  """
1698 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
1699 739be818 Michael Hanselmann
1700 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1701 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1702 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1703 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1704 739be818 Michael Hanselmann
1705 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
1706 739be818 Michael Hanselmann
1707 739be818 Michael Hanselmann
1708 cd50653c Guido Trotter
def GetDaemonPort(daemon_name):
1709 cd50653c Guido Trotter
  """Get the daemon port for this cluster.
1710 4a8b186a Michael Hanselmann

1711 4a8b186a Michael Hanselmann
  Note that this routine does not read a ganeti-specific file, but
1712 58885d79 Iustin Pop
  instead uses C{socket.getservbyname} to allow pre-customization of
1713 4a8b186a Michael Hanselmann
  this parameter outside of Ganeti.
1714 4a8b186a Michael Hanselmann

1715 cd50653c Guido Trotter
  @type daemon_name: string
1716 cd50653c Guido Trotter
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
1717 58885d79 Iustin Pop
  @rtype: int
1718 58885d79 Iustin Pop

1719 4a8b186a Michael Hanselmann
  """
1720 cd50653c Guido Trotter
  if daemon_name not in constants.DAEMONS_PORTS:
1721 cd50653c Guido Trotter
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
1722 cd50653c Guido Trotter
1723 cd50653c Guido Trotter
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
1724 4a8b186a Michael Hanselmann
  try:
1725 cd50653c Guido Trotter
    port = socket.getservbyname(daemon_name, proto)
1726 4a8b186a Michael Hanselmann
  except socket.error:
1727 cd50653c Guido Trotter
    port = default_port
1728 4a8b186a Michael Hanselmann
1729 4a8b186a Michael Hanselmann
  return port
1730 4a8b186a Michael Hanselmann
1731 4a8b186a Michael Hanselmann
1732 d21d09d6 Iustin Pop
def SetupLogging(logfile, debug=False, stderr_logging=False, program="",
1733 d21d09d6 Iustin Pop
                 multithreaded=False):
1734 82d9caef Iustin Pop
  """Configures the logging module.
1735 82d9caef Iustin Pop

1736 58885d79 Iustin Pop
  @type logfile: str
1737 58885d79 Iustin Pop
  @param logfile: the filename to which we should log
1738 58885d79 Iustin Pop
  @type debug: boolean
1739 58885d79 Iustin Pop
  @param debug: whether to enable debug messages too or
1740 58885d79 Iustin Pop
      only those at C{INFO} and above level
1741 58885d79 Iustin Pop
  @type stderr_logging: boolean
1742 58885d79 Iustin Pop
  @param stderr_logging: whether we should also log to the standard error
1743 58885d79 Iustin Pop
  @type program: str
1744 58885d79 Iustin Pop
  @param program: the name under which we should log messages
1745 d21d09d6 Iustin Pop
  @type multithreaded: boolean
1746 d21d09d6 Iustin Pop
  @param multithreaded: if True, will add the thread name to the log file
1747 58885d79 Iustin Pop
  @raise EnvironmentError: if we can't open the log file and
1748 58885d79 Iustin Pop
      stderr logging is disabled
1749 58885d79 Iustin Pop

1750 82d9caef Iustin Pop
  """
1751 d21d09d6 Iustin Pop
  fmt = "%(asctime)s: " + program + " pid=%(process)d"
1752 d21d09d6 Iustin Pop
  if multithreaded:
1753 d21d09d6 Iustin Pop
    fmt += "/%(threadName)s"
1754 82d9caef Iustin Pop
  if debug:
1755 d21d09d6 Iustin Pop
    fmt += " %(module)s:%(lineno)s"
1756 d21d09d6 Iustin Pop
  fmt += " %(levelname)s %(message)s"
1757 82d9caef Iustin Pop
  formatter = logging.Formatter(fmt)
1758 82d9caef Iustin Pop
1759 82d9caef Iustin Pop
  root_logger = logging.getLogger("")
1760 82d9caef Iustin Pop
  root_logger.setLevel(logging.NOTSET)
1761 82d9caef Iustin Pop
1762 6346a9e5 Michael Hanselmann
  # Remove all previously setup handlers
1763 6346a9e5 Michael Hanselmann
  for handler in root_logger.handlers:
1764 7d88772a Iustin Pop
    handler.close()
1765 6346a9e5 Michael Hanselmann
    root_logger.removeHandler(handler)
1766 6346a9e5 Michael Hanselmann
1767 82d9caef Iustin Pop
  if stderr_logging:
1768 82d9caef Iustin Pop
    stderr_handler = logging.StreamHandler()
1769 82d9caef Iustin Pop
    stderr_handler.setFormatter(formatter)
1770 82d9caef Iustin Pop
    if debug:
1771 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.NOTSET)
1772 82d9caef Iustin Pop
    else:
1773 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.CRITICAL)
1774 82d9caef Iustin Pop
    root_logger.addHandler(stderr_handler)
1775 82d9caef Iustin Pop
1776 82d9caef Iustin Pop
  # this can fail, if the logging directories are not setup or we have
1777 82d9caef Iustin Pop
  # a permisssion problem; in this case, it's best to log but ignore
1778 82d9caef Iustin Pop
  # the error if stderr_logging is True, and if false we re-raise the
1779 82d9caef Iustin Pop
  # exception since otherwise we could run but without any logs at all
1780 82d9caef Iustin Pop
  try:
1781 82d9caef Iustin Pop
    logfile_handler = logging.FileHandler(logfile)
1782 82d9caef Iustin Pop
    logfile_handler.setFormatter(formatter)
1783 82d9caef Iustin Pop
    if debug:
1784 82d9caef Iustin Pop
      logfile_handler.setLevel(logging.DEBUG)
1785 82d9caef Iustin Pop
    else:
1786 82d9caef Iustin Pop
      logfile_handler.setLevel(logging.INFO)
1787 82d9caef Iustin Pop
    root_logger.addHandler(logfile_handler)
1788 d21d09d6 Iustin Pop
  except EnvironmentError:
1789 82d9caef Iustin Pop
    if stderr_logging:
1790 82d9caef Iustin Pop
      logging.exception("Failed to enable logging to file '%s'", logfile)
1791 82d9caef Iustin Pop
    else:
1792 82d9caef Iustin Pop
      # we need to re-raise the exception
1793 82d9caef Iustin Pop
      raise
1794 82d9caef Iustin Pop
1795 016d04b3 Michael Hanselmann
1796 da961187 Guido Trotter
def IsNormAbsPath(path):
1797 17c61836 Guido Trotter
  """Check whether a path is absolute and also normalized
1798 da961187 Guido Trotter

1799 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
1800 da961187 Guido Trotter

1801 da961187 Guido Trotter
  """
1802 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
1803 82d9caef Iustin Pop
1804 016d04b3 Michael Hanselmann
1805 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
1806 f65f63ef Iustin Pop
  """Return the last lines from a file.
1807 f65f63ef Iustin Pop

1808 f65f63ef Iustin Pop
  @note: this function will only read and parse the last 4KB of
1809 f65f63ef Iustin Pop
      the file; if the lines are very long, it could be that less
1810 f65f63ef Iustin Pop
      than the requested number of lines are returned
1811 f65f63ef Iustin Pop

1812 f65f63ef Iustin Pop
  @param fname: the file name
1813 f65f63ef Iustin Pop
  @type lines: int
1814 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
1815 f65f63ef Iustin Pop

1816 f65f63ef Iustin Pop
  """
1817 f65f63ef Iustin Pop
  fd = open(fname, "r")
1818 f65f63ef Iustin Pop
  try:
1819 f65f63ef Iustin Pop
    fd.seek(0, 2)
1820 f65f63ef Iustin Pop
    pos = fd.tell()
1821 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
1822 f65f63ef Iustin Pop
    fd.seek(pos, 0)
1823 f65f63ef Iustin Pop
    raw_data = fd.read()
1824 f65f63ef Iustin Pop
  finally:
1825 f65f63ef Iustin Pop
    fd.close()
1826 f65f63ef Iustin Pop
1827 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
1828 f65f63ef Iustin Pop
  return rows[-lines:]
1829 f65f63ef Iustin Pop
1830 f65f63ef Iustin Pop
1831 26f15862 Iustin Pop
def SafeEncode(text):
1832 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
1833 26f15862 Iustin Pop

1834 26f15862 Iustin Pop
  This function mangles the input string and returns a version that
1835 d392fa34 Iustin Pop
  should be safe to display/encode as ASCII. To this end, we first
1836 26f15862 Iustin Pop
  convert it to ASCII using the 'backslashreplace' encoding which
1837 d392fa34 Iustin Pop
  should get rid of any non-ASCII chars, and then we process it
1838 d392fa34 Iustin Pop
  through a loop copied from the string repr sources in the python; we
1839 d392fa34 Iustin Pop
  don't use string_escape anymore since that escape single quotes and
1840 d392fa34 Iustin Pop
  backslashes too, and that is too much; and that escaping is not
1841 d392fa34 Iustin Pop
  stable, i.e. string_escape(string_escape(x)) != string_escape(x).
1842 26f15862 Iustin Pop

1843 26f15862 Iustin Pop
  @type text: str or unicode
1844 26f15862 Iustin Pop
  @param text: input data
1845 26f15862 Iustin Pop
  @rtype: str
1846 26f15862 Iustin Pop
  @return: a safe version of text
1847 26f15862 Iustin Pop

1848 26f15862 Iustin Pop
  """
1849 d392fa34 Iustin Pop
  if isinstance(text, unicode):
1850 5bbd3f7f Michael Hanselmann
    # only if unicode; if str already, we handle it below
1851 d392fa34 Iustin Pop
    text = text.encode('ascii', 'backslashreplace')
1852 d392fa34 Iustin Pop
  resu = ""
1853 d392fa34 Iustin Pop
  for char in text:
1854 d392fa34 Iustin Pop
    c = ord(char)
1855 d392fa34 Iustin Pop
    if char  == '\t':
1856 d392fa34 Iustin Pop
      resu += r'\t'
1857 d392fa34 Iustin Pop
    elif char == '\n':
1858 d392fa34 Iustin Pop
      resu += r'\n'
1859 d392fa34 Iustin Pop
    elif char == '\r':
1860 d392fa34 Iustin Pop
      resu += r'\'r'
1861 d392fa34 Iustin Pop
    elif c < 32 or c >= 127: # non-printable
1862 d392fa34 Iustin Pop
      resu += "\\x%02x" % (c & 0xff)
1863 d392fa34 Iustin Pop
    else:
1864 d392fa34 Iustin Pop
      resu += char
1865 d392fa34 Iustin Pop
  return resu
1866 26f15862 Iustin Pop
1867 26f15862 Iustin Pop
1868 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
1869 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
1870 3f6a47a8 Michael Hanselmann

1871 3f6a47a8 Michael Hanselmann
  @type value: int
1872 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
1873 3f6a47a8 Michael Hanselmann
  @rtype: int
1874 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
1875 3f6a47a8 Michael Hanselmann

1876 3f6a47a8 Michael Hanselmann
  """
1877 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
1878 3f6a47a8 Michael Hanselmann
1879 3f6a47a8 Michael Hanselmann
1880 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
1881 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
1882 3f6a47a8 Michael Hanselmann

1883 3f6a47a8 Michael Hanselmann
  @type path: string
1884 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
1885 3f6a47a8 Michael Hanselmann
  @rtype: int
1886 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
1887 3f6a47a8 Michael Hanselmann

1888 3f6a47a8 Michael Hanselmann
  """
1889 3f6a47a8 Michael Hanselmann
  size = 0
1890 3f6a47a8 Michael Hanselmann
1891 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
1892 2a887df9 Michael Hanselmann
    for filename in files:
1893 2a887df9 Michael Hanselmann
      st = os.lstat(os.path.join(curpath, filename))
1894 3f6a47a8 Michael Hanselmann
      size += st.st_size
1895 3f6a47a8 Michael Hanselmann
1896 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
1897 3f6a47a8 Michael Hanselmann
1898 3f6a47a8 Michael Hanselmann
1899 620a85fd Iustin Pop
def GetFilesystemStats(path):
1900 620a85fd Iustin Pop
  """Returns the total and free space on a filesystem.
1901 3f6a47a8 Michael Hanselmann

1902 3f6a47a8 Michael Hanselmann
  @type path: string
1903 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
1904 3f6a47a8 Michael Hanselmann
  @rtype: int
1905 620a85fd Iustin Pop
  @return: tuple of (Total space, Free space) in mebibytes
1906 3f6a47a8 Michael Hanselmann

1907 3f6a47a8 Michael Hanselmann
  """
1908 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
1909 3f6a47a8 Michael Hanselmann
1910 620a85fd Iustin Pop
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
1911 620a85fd Iustin Pop
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
1912 620a85fd Iustin Pop
  return (tsize, fsize)
1913 3f6a47a8 Michael Hanselmann
1914 3f6a47a8 Michael Hanselmann
1915 7996a135 Iustin Pop
def LockedMethod(fn):
1916 7996a135 Iustin Pop
  """Synchronized object access decorator.
1917 7996a135 Iustin Pop

1918 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
1919 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
1920 7996a135 Iustin Pop

1921 7996a135 Iustin Pop
  """
1922 e67bd559 Michael Hanselmann
  def _LockDebug(*args, **kwargs):
1923 e67bd559 Michael Hanselmann
    if debug_locks:
1924 e67bd559 Michael Hanselmann
      logging.debug(*args, **kwargs)
1925 e67bd559 Michael Hanselmann
1926 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
1927 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
1928 7996a135 Iustin Pop
    lock = self._lock
1929 e67bd559 Michael Hanselmann
    _LockDebug("Waiting for %s", lock)
1930 7996a135 Iustin Pop
    lock.acquire()
1931 7996a135 Iustin Pop
    try:
1932 e67bd559 Michael Hanselmann
      _LockDebug("Acquired %s", lock)
1933 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
1934 7996a135 Iustin Pop
    finally:
1935 e67bd559 Michael Hanselmann
      _LockDebug("Releasing %s", lock)
1936 7996a135 Iustin Pop
      lock.release()
1937 e67bd559 Michael Hanselmann
      _LockDebug("Released %s", lock)
1938 7996a135 Iustin Pop
    return result
1939 7996a135 Iustin Pop
  return wrapper
1940 eb0f0ce0 Michael Hanselmann
1941 eb0f0ce0 Michael Hanselmann
1942 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
1943 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
1944 eb0f0ce0 Michael Hanselmann

1945 58885d79 Iustin Pop
  @type fd: int
1946 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
1947 58885d79 Iustin Pop

1948 eb0f0ce0 Michael Hanselmann
  """
1949 eb0f0ce0 Michael Hanselmann
  try:
1950 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
1951 eb0f0ce0 Michael Hanselmann
  except IOError, err:
1952 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
1953 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
1954 eb0f0ce0 Michael Hanselmann
    raise
1955 de499029 Michael Hanselmann
1956 de499029 Michael Hanselmann
1957 3b813dd2 Iustin Pop
def FormatTime(val):
1958 3b813dd2 Iustin Pop
  """Formats a time value.
1959 3b813dd2 Iustin Pop

1960 3b813dd2 Iustin Pop
  @type val: float or None
1961 3b813dd2 Iustin Pop
  @param val: the timestamp as returned by time.time()
1962 3b813dd2 Iustin Pop
  @return: a string value or N/A if we don't have a valid timestamp
1963 3b813dd2 Iustin Pop

1964 3b813dd2 Iustin Pop
  """
1965 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
1966 3b813dd2 Iustin Pop
    return "N/A"
1967 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
1968 3b813dd2 Iustin Pop
  # platforms
1969 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
1970 3b813dd2 Iustin Pop
1971 3b813dd2 Iustin Pop
1972 5cbe43a5 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
1973 05e50653 Michael Hanselmann
  """Reads the watcher pause file.
1974 05e50653 Michael Hanselmann

1975 5cbe43a5 Michael Hanselmann
  @type filename: string
1976 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
1977 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
1978 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
1979 5cbe43a5 Michael Hanselmann
  @type remove_after: int
1980 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
1981 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
1982 5cbe43a5 Michael Hanselmann

1983 05e50653 Michael Hanselmann
  """
1984 05e50653 Michael Hanselmann
  if now is None:
1985 05e50653 Michael Hanselmann
    now = time.time()
1986 05e50653 Michael Hanselmann
1987 05e50653 Michael Hanselmann
  try:
1988 05e50653 Michael Hanselmann
    value = ReadFile(filename)
1989 05e50653 Michael Hanselmann
  except IOError, err:
1990 05e50653 Michael Hanselmann
    if err.errno != errno.ENOENT:
1991 05e50653 Michael Hanselmann
      raise
1992 05e50653 Michael Hanselmann
    value = None
1993 05e50653 Michael Hanselmann
1994 05e50653 Michael Hanselmann
  if value is not None:
1995 05e50653 Michael Hanselmann
    try:
1996 05e50653 Michael Hanselmann
      value = int(value)
1997 05e50653 Michael Hanselmann
    except ValueError:
1998 5cbe43a5 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
1999 5cbe43a5 Michael Hanselmann
                       " removing it"), filename)
2000 5cbe43a5 Michael Hanselmann
      RemoveFile(filename)
2001 05e50653 Michael Hanselmann
      value = None
2002 05e50653 Michael Hanselmann
2003 05e50653 Michael Hanselmann
    if value is not None:
2004 5cbe43a5 Michael Hanselmann
      # Remove file if it's outdated
2005 5cbe43a5 Michael Hanselmann
      if now > (value + remove_after):
2006 5cbe43a5 Michael Hanselmann
        RemoveFile(filename)
2007 5cbe43a5 Michael Hanselmann
        value = None
2008 5cbe43a5 Michael Hanselmann
2009 5cbe43a5 Michael Hanselmann
      elif now > value:
2010 05e50653 Michael Hanselmann
        value = None
2011 05e50653 Michael Hanselmann
2012 05e50653 Michael Hanselmann
  return value
2013 05e50653 Michael Hanselmann
2014 05e50653 Michael Hanselmann
2015 de0ea66b Michael Hanselmann
class RetryTimeout(Exception):
2016 de0ea66b Michael Hanselmann
  """Retry loop timed out.
2017 de0ea66b Michael Hanselmann

2018 de0ea66b Michael Hanselmann
  """
2019 de0ea66b Michael Hanselmann
2020 de0ea66b Michael Hanselmann
2021 de0ea66b Michael Hanselmann
class RetryAgain(Exception):
2022 de0ea66b Michael Hanselmann
  """Retry again.
2023 de0ea66b Michael Hanselmann

2024 de0ea66b Michael Hanselmann
  """
2025 de0ea66b Michael Hanselmann
2026 de0ea66b Michael Hanselmann
2027 de0ea66b Michael Hanselmann
class _RetryDelayCalculator(object):
2028 de0ea66b Michael Hanselmann
  """Calculator for increasing delays.
2029 de0ea66b Michael Hanselmann

2030 de0ea66b Michael Hanselmann
  """
2031 de0ea66b Michael Hanselmann
  __slots__ = [
2032 de0ea66b Michael Hanselmann
    "_factor",
2033 de0ea66b Michael Hanselmann
    "_limit",
2034 de0ea66b Michael Hanselmann
    "_next",
2035 de0ea66b Michael Hanselmann
    "_start",
2036 de0ea66b Michael Hanselmann
    ]
2037 de0ea66b Michael Hanselmann
2038 de0ea66b Michael Hanselmann
  def __init__(self, start, factor, limit):
2039 de0ea66b Michael Hanselmann
    """Initializes this class.
2040 de0ea66b Michael Hanselmann

2041 de0ea66b Michael Hanselmann
    @type start: float
2042 de0ea66b Michael Hanselmann
    @param start: Initial delay
2043 de0ea66b Michael Hanselmann
    @type factor: float
2044 de0ea66b Michael Hanselmann
    @param factor: Factor for delay increase
2045 de0ea66b Michael Hanselmann
    @type limit: float or None
2046 de0ea66b Michael Hanselmann
    @param limit: Upper limit for delay or None for no limit
2047 de0ea66b Michael Hanselmann

2048 de0ea66b Michael Hanselmann
    """
2049 de0ea66b Michael Hanselmann
    assert start > 0.0
2050 de0ea66b Michael Hanselmann
    assert factor >= 1.0
2051 de0ea66b Michael Hanselmann
    assert limit is None or limit >= 0.0
2052 de0ea66b Michael Hanselmann
2053 de0ea66b Michael Hanselmann
    self._start = start
2054 de0ea66b Michael Hanselmann
    self._factor = factor
2055 de0ea66b Michael Hanselmann
    self._limit = limit
2056 de0ea66b Michael Hanselmann
2057 de0ea66b Michael Hanselmann
    self._next = start
2058 de0ea66b Michael Hanselmann
2059 de0ea66b Michael Hanselmann
  def __call__(self):
2060 de0ea66b Michael Hanselmann
    """Returns current delay and calculates the next one.
2061 de0ea66b Michael Hanselmann

2062 de0ea66b Michael Hanselmann
    """
2063 de0ea66b Michael Hanselmann
    current = self._next
2064 de0ea66b Michael Hanselmann
2065 de0ea66b Michael Hanselmann
    # Update for next run
2066 de0ea66b Michael Hanselmann
    if self._limit is None or self._next < self._limit:
2067 de0ea66b Michael Hanselmann
      self._next = max(self._limit, self._next * self._factor)
2068 de0ea66b Michael Hanselmann
2069 de0ea66b Michael Hanselmann
    return current
2070 de0ea66b Michael Hanselmann
2071 de0ea66b Michael Hanselmann
2072 de0ea66b Michael Hanselmann
#: Special delay to specify whole remaining timeout
2073 de0ea66b Michael Hanselmann
RETRY_REMAINING_TIME = object()
2074 de0ea66b Michael Hanselmann
2075 de0ea66b Michael Hanselmann
2076 de0ea66b Michael Hanselmann
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep,
2077 de0ea66b Michael Hanselmann
          _time_fn=time.time):
2078 de0ea66b Michael Hanselmann
  """Call a function repeatedly until it succeeds.
2079 de0ea66b Michael Hanselmann

2080 de0ea66b Michael Hanselmann
  The function C{fn} is called repeatedly until it doesn't throw L{RetryAgain}
2081 de0ea66b Michael Hanselmann
  anymore. Between calls a delay, specified by C{delay}, is inserted. After a
2082 de0ea66b Michael Hanselmann
  total of C{timeout} seconds, this function throws L{RetryTimeout}.
2083 de0ea66b Michael Hanselmann

2084 de0ea66b Michael Hanselmann
  C{delay} can be one of the following:
2085 de0ea66b Michael Hanselmann
    - callable returning the delay length as a float
2086 de0ea66b Michael Hanselmann
    - Tuple of (start, factor, limit)
2087 de0ea66b Michael Hanselmann
    - L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is
2088 de0ea66b Michael Hanselmann
      useful when overriding L{wait_fn} to wait for an external event)
2089 de0ea66b Michael Hanselmann
    - A static delay as a number (int or float)
2090 de0ea66b Michael Hanselmann

2091 de0ea66b Michael Hanselmann
  @type fn: callable
2092 de0ea66b Michael Hanselmann
  @param fn: Function to be called
2093 de0ea66b Michael Hanselmann
  @param delay: Either a callable (returning the delay), a tuple of (start,
2094 de0ea66b Michael Hanselmann
                factor, limit) (see L{_RetryDelayCalculator}),
2095 de0ea66b Michael Hanselmann
                L{RETRY_REMAINING_TIME} or a number (int or float)
2096 de0ea66b Michael Hanselmann
  @type timeout: float
2097 de0ea66b Michael Hanselmann
  @param timeout: Total timeout
2098 de0ea66b Michael Hanselmann
  @type wait_fn: callable
2099 de0ea66b Michael Hanselmann
  @param wait_fn: Waiting function
2100 de0ea66b Michael Hanselmann
  @return: Return value of function
2101 de0ea66b Michael Hanselmann

2102 de0ea66b Michael Hanselmann
  """
2103 de0ea66b Michael Hanselmann
  assert callable(fn)
2104 de0ea66b Michael Hanselmann
  assert callable(wait_fn)
2105 de0ea66b Michael Hanselmann
  assert callable(_time_fn)
2106 de0ea66b Michael Hanselmann
2107 de0ea66b Michael Hanselmann
  if args is None:
2108 de0ea66b Michael Hanselmann
    args = []
2109 de0ea66b Michael Hanselmann
2110 de0ea66b Michael Hanselmann
  end_time = _time_fn() + timeout
2111 de0ea66b Michael Hanselmann
2112 de0ea66b Michael Hanselmann
  if callable(delay):
2113 de0ea66b Michael Hanselmann
    # External function to calculate delay
2114 de0ea66b Michael Hanselmann
    calc_delay = delay
2115 de0ea66b Michael Hanselmann
2116 de0ea66b Michael Hanselmann
  elif isinstance(delay, (tuple, list)):
2117 de0ea66b Michael Hanselmann
    # Increasing delay with optional upper boundary
2118 de0ea66b Michael Hanselmann
    (start, factor, limit) = delay
2119 de0ea66b Michael Hanselmann
    calc_delay = _RetryDelayCalculator(start, factor, limit)
2120 de0ea66b Michael Hanselmann
2121 de0ea66b Michael Hanselmann
  elif delay is RETRY_REMAINING_TIME:
2122 de0ea66b Michael Hanselmann
    # Always use the remaining time
2123 de0ea66b Michael Hanselmann
    calc_delay = None
2124 de0ea66b Michael Hanselmann
2125 de0ea66b Michael Hanselmann
  else:
2126 de0ea66b Michael Hanselmann
    # Static delay
2127 de0ea66b Michael Hanselmann
    calc_delay = lambda: delay
2128 de0ea66b Michael Hanselmann
2129 de0ea66b Michael Hanselmann
  assert calc_delay is None or callable(calc_delay)
2130 de0ea66b Michael Hanselmann
2131 de0ea66b Michael Hanselmann
  while True:
2132 de0ea66b Michael Hanselmann
    try:
2133 de0ea66b Michael Hanselmann
      return fn(*args)
2134 de0ea66b Michael Hanselmann
    except RetryAgain:
2135 de0ea66b Michael Hanselmann
      pass
2136 de0ea66b Michael Hanselmann
2137 de0ea66b Michael Hanselmann
    remaining_time = end_time - _time_fn()
2138 de0ea66b Michael Hanselmann
2139 de0ea66b Michael Hanselmann
    if remaining_time < 0.0:
2140 de0ea66b Michael Hanselmann
      raise RetryTimeout()
2141 de0ea66b Michael Hanselmann
2142 de0ea66b Michael Hanselmann
    assert remaining_time >= 0.0
2143 de0ea66b Michael Hanselmann
2144 de0ea66b Michael Hanselmann
    if calc_delay is None:
2145 de0ea66b Michael Hanselmann
      wait_fn(remaining_time)
2146 de0ea66b Michael Hanselmann
    else:
2147 de0ea66b Michael Hanselmann
      current_delay = calc_delay()
2148 de0ea66b Michael Hanselmann
      if current_delay > 0.0:
2149 de0ea66b Michael Hanselmann
        wait_fn(current_delay)
2150 de0ea66b Michael Hanselmann
2151 de0ea66b Michael Hanselmann
2152 a87b4824 Michael Hanselmann
class FileLock(object):
2153 a87b4824 Michael Hanselmann
  """Utility class for file locks.
2154 a87b4824 Michael Hanselmann

2155 a87b4824 Michael Hanselmann
  """
2156 a87b4824 Michael Hanselmann
  def __init__(self, filename):
2157 58885d79 Iustin Pop
    """Constructor for FileLock.
2158 58885d79 Iustin Pop

2159 58885d79 Iustin Pop
    This will open the file denoted by the I{filename} argument.
2160 58885d79 Iustin Pop

2161 58885d79 Iustin Pop
    @type filename: str
2162 58885d79 Iustin Pop
    @param filename: path to the file to be locked
2163 58885d79 Iustin Pop

2164 58885d79 Iustin Pop
    """
2165 a87b4824 Michael Hanselmann
    self.filename = filename
2166 a87b4824 Michael Hanselmann
    self.fd = open(self.filename, "w")
2167 a87b4824 Michael Hanselmann
2168 a87b4824 Michael Hanselmann
  def __del__(self):
2169 a87b4824 Michael Hanselmann
    self.Close()
2170 a87b4824 Michael Hanselmann
2171 a87b4824 Michael Hanselmann
  def Close(self):
2172 58885d79 Iustin Pop
    """Close the file and release the lock.
2173 58885d79 Iustin Pop

2174 58885d79 Iustin Pop
    """
2175 a87b4824 Michael Hanselmann
    if self.fd:
2176 a87b4824 Michael Hanselmann
      self.fd.close()
2177 a87b4824 Michael Hanselmann
      self.fd = None
2178 a87b4824 Michael Hanselmann
2179 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
2180 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
2181 aa74b828 Michael Hanselmann

2182 aa74b828 Michael Hanselmann
    @type flag: int
2183 58885d79 Iustin Pop
    @param flag: operation flag
2184 aa74b828 Michael Hanselmann
    @type blocking: bool
2185 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
2186 aa74b828 Michael Hanselmann
    @type timeout: None or float
2187 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
2188 aa74b828 Michael Hanselmann
                    non-blocking mode).
2189 aa74b828 Michael Hanselmann
    @type errmsg: string
2190 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
2191 aa74b828 Michael Hanselmann

2192 aa74b828 Michael Hanselmann
    """
2193 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
2194 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
2195 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
2196 a87b4824 Michael Hanselmann
2197 aa74b828 Michael Hanselmann
    if timeout is not None:
2198 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
2199 aa74b828 Michael Hanselmann
      timeout_end = time.time() + timeout
2200 a87b4824 Michael Hanselmann
2201 aa74b828 Michael Hanselmann
    # Blocking doesn't have effect with timeout
2202 aa74b828 Michael Hanselmann
    elif not blocking:
2203 aa74b828 Michael Hanselmann
      flag |= fcntl.LOCK_NB
2204 aa74b828 Michael Hanselmann
      timeout_end = None
2205 aa74b828 Michael Hanselmann
2206 31892b4c Michael Hanselmann
    # TODO: Convert to utils.Retry
2207 31892b4c Michael Hanselmann
2208 aa74b828 Michael Hanselmann
    retry = True
2209 aa74b828 Michael Hanselmann
    while retry:
2210 aa74b828 Michael Hanselmann
      try:
2211 aa74b828 Michael Hanselmann
        fcntl.flock(self.fd, flag)
2212 aa74b828 Michael Hanselmann
        retry = False
2213 aa74b828 Michael Hanselmann
      except IOError, err:
2214 aa74b828 Michael Hanselmann
        if err.errno in (errno.EAGAIN, ):
2215 aa74b828 Michael Hanselmann
          if timeout_end is not None and time.time() < timeout_end:
2216 aa74b828 Michael Hanselmann
            # Wait before trying again
2217 aa74b828 Michael Hanselmann
            time.sleep(max(0.1, min(1.0, timeout)))
2218 aa74b828 Michael Hanselmann
          else:
2219 aa74b828 Michael Hanselmann
            raise errors.LockError(errmsg)
2220 aa74b828 Michael Hanselmann
        else:
2221 aa74b828 Michael Hanselmann
          logging.exception("fcntl.flock failed")
2222 aa74b828 Michael Hanselmann
          raise
2223 aa74b828 Michael Hanselmann
2224 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
2225 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
2226 a87b4824 Michael Hanselmann

2227 58885d79 Iustin Pop
    @type blocking: boolean
2228 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2229 58885d79 Iustin Pop
        can lock the file or return immediately
2230 58885d79 Iustin Pop
    @type timeout: int or None
2231 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2232 58885d79 Iustin Pop
        (in blocking mode)
2233 58885d79 Iustin Pop

2234 a87b4824 Michael Hanselmann
    """
2235 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
2236 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
2237 a87b4824 Michael Hanselmann
2238 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
2239 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
2240 a87b4824 Michael Hanselmann

2241 58885d79 Iustin Pop
    @type blocking: boolean
2242 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2243 58885d79 Iustin Pop
        can lock the file or return immediately
2244 58885d79 Iustin Pop
    @type timeout: int or None
2245 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2246 58885d79 Iustin Pop
        (in blocking mode)
2247 58885d79 Iustin Pop

2248 a87b4824 Michael Hanselmann
    """
2249 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
2250 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
2251 a87b4824 Michael Hanselmann
2252 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
2253 a87b4824 Michael Hanselmann
    """Unlocks the file.
2254 a87b4824 Michael Hanselmann

2255 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
2256 58885d79 Iustin Pop
    operation::
2257 58885d79 Iustin Pop

2258 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
2259 58885d79 Iustin Pop
      operations.
2260 58885d79 Iustin Pop

2261 58885d79 Iustin Pop
    @type blocking: boolean
2262 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2263 58885d79 Iustin Pop
        can lock the file or return immediately
2264 58885d79 Iustin Pop
    @type timeout: int or None
2265 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2266 58885d79 Iustin Pop
        (in blocking mode)
2267 a87b4824 Michael Hanselmann

2268 a87b4824 Michael Hanselmann
    """
2269 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
2270 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
2271 a87b4824 Michael Hanselmann
2272 a87b4824 Michael Hanselmann
2273 451575de Guido Trotter
def SignalHandled(signums):
2274 451575de Guido Trotter
  """Signal Handled decoration.
2275 451575de Guido Trotter

2276 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
2277 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
2278 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
2279 451575de Guido Trotter
  objects as values.
2280 451575de Guido Trotter

2281 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
2282 451575de Guido Trotter
  with different handlers.
2283 451575de Guido Trotter

2284 451575de Guido Trotter
  @type signums: list
2285 451575de Guido Trotter
  @param signums: signals to intercept
2286 451575de Guido Trotter

2287 451575de Guido Trotter
  """
2288 451575de Guido Trotter
  def wrap(fn):
2289 451575de Guido Trotter
    def sig_function(*args, **kwargs):
2290 451575de Guido Trotter
      assert 'signal_handlers' not in kwargs or \
2291 451575de Guido Trotter
             kwargs['signal_handlers'] is None or \
2292 451575de Guido Trotter
             isinstance(kwargs['signal_handlers'], dict), \
2293 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
2294 451575de Guido Trotter
      if 'signal_handlers' in kwargs and kwargs['signal_handlers'] is not None:
2295 451575de Guido Trotter
        signal_handlers = kwargs['signal_handlers']
2296 451575de Guido Trotter
      else:
2297 451575de Guido Trotter
        signal_handlers = {}
2298 451575de Guido Trotter
        kwargs['signal_handlers'] = signal_handlers
2299 451575de Guido Trotter
      sighandler = SignalHandler(signums)
2300 451575de Guido Trotter
      try:
2301 451575de Guido Trotter
        for sig in signums:
2302 451575de Guido Trotter
          signal_handlers[sig] = sighandler
2303 451575de Guido Trotter
        return fn(*args, **kwargs)
2304 451575de Guido Trotter
      finally:
2305 451575de Guido Trotter
        sighandler.Reset()
2306 451575de Guido Trotter
    return sig_function
2307 451575de Guido Trotter
  return wrap
2308 451575de Guido Trotter
2309 451575de Guido Trotter
2310 de499029 Michael Hanselmann
class SignalHandler(object):
2311 de499029 Michael Hanselmann
  """Generic signal handler class.
2312 de499029 Michael Hanselmann

2313 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
2314 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
2315 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
2316 58885d79 Iustin Pop
  signal was sent.
2317 58885d79 Iustin Pop

2318 58885d79 Iustin Pop
  @type signum: list
2319 58885d79 Iustin Pop
  @ivar signum: the signals we handle
2320 58885d79 Iustin Pop
  @type called: boolean
2321 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
2322 de499029 Michael Hanselmann

2323 de499029 Michael Hanselmann
  """
2324 de499029 Michael Hanselmann
  def __init__(self, signum):
2325 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
2326 de499029 Michael Hanselmann

2327 58885d79 Iustin Pop
    @type signum: int or list of ints
2328 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
2329 de499029 Michael Hanselmann

2330 de499029 Michael Hanselmann
    """
2331 6c52849e Guido Trotter
    self.signum = set(signum)
2332 de499029 Michael Hanselmann
    self.called = False
2333 de499029 Michael Hanselmann
2334 de499029 Michael Hanselmann
    self._previous = {}
2335 de499029 Michael Hanselmann
    try:
2336 de499029 Michael Hanselmann
      for signum in self.signum:
2337 de499029 Michael Hanselmann
        # Setup handler
2338 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
2339 de499029 Michael Hanselmann
        try:
2340 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
2341 de499029 Michael Hanselmann
        except:
2342 de499029 Michael Hanselmann
          # Restore previous handler
2343 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
2344 de499029 Michael Hanselmann
          raise
2345 de499029 Michael Hanselmann
    except:
2346 de499029 Michael Hanselmann
      # Reset all handlers
2347 de499029 Michael Hanselmann
      self.Reset()
2348 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
2349 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
2350 de499029 Michael Hanselmann
      raise
2351 de499029 Michael Hanselmann
2352 de499029 Michael Hanselmann
  def __del__(self):
2353 de499029 Michael Hanselmann
    self.Reset()
2354 de499029 Michael Hanselmann
2355 de499029 Michael Hanselmann
  def Reset(self):
2356 de499029 Michael Hanselmann
    """Restore previous handler.
2357 de499029 Michael Hanselmann

2358 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
2359 58885d79 Iustin Pop

2360 de499029 Michael Hanselmann
    """
2361 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
2362 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
2363 de499029 Michael Hanselmann
      # If successful, remove from dict
2364 de499029 Michael Hanselmann
      del self._previous[signum]
2365 de499029 Michael Hanselmann
2366 de499029 Michael Hanselmann
  def Clear(self):
2367 58885d79 Iustin Pop
    """Unsets the L{called} flag.
2368 de499029 Michael Hanselmann

2369 de499029 Michael Hanselmann
    This function can be used in case a signal may arrive several times.
2370 de499029 Michael Hanselmann

2371 de499029 Michael Hanselmann
    """
2372 de499029 Michael Hanselmann
    self.called = False
2373 de499029 Michael Hanselmann
2374 de499029 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
2375 de499029 Michael Hanselmann
    """Actual signal handling function.
2376 de499029 Michael Hanselmann

2377 de499029 Michael Hanselmann
    """
2378 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
2379 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
2380 de499029 Michael Hanselmann
    self.called = True
2381 a2d2e1a7 Iustin Pop
2382 a2d2e1a7 Iustin Pop
2383 a2d2e1a7 Iustin Pop
class FieldSet(object):
2384 a2d2e1a7 Iustin Pop
  """A simple field set.
2385 a2d2e1a7 Iustin Pop

2386 a2d2e1a7 Iustin Pop
  Among the features are:
2387 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
2388 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
2389 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
2390 a2d2e1a7 Iustin Pop

2391 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
2392 a2d2e1a7 Iustin Pop

2393 a2d2e1a7 Iustin Pop
  """
2394 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
2395 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
2396 a2d2e1a7 Iustin Pop
2397 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
2398 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
2399 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
2400 a2d2e1a7 Iustin Pop
2401 a2d2e1a7 Iustin Pop
  def Matches(self, field):
2402 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
2403 a2d2e1a7 Iustin Pop

2404 a2d2e1a7 Iustin Pop
    @type field: str
2405 a2d2e1a7 Iustin Pop
    @param field: the string to match
2406 a2d2e1a7 Iustin Pop
    @return: either False or a regular expression match object
2407 a2d2e1a7 Iustin Pop

2408 a2d2e1a7 Iustin Pop
    """
2409 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
2410 a2d2e1a7 Iustin Pop
      return m
2411 a2d2e1a7 Iustin Pop
    return False
2412 a2d2e1a7 Iustin Pop
2413 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
2414 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
2415 a2d2e1a7 Iustin Pop

2416 a2d2e1a7 Iustin Pop
    @type items: list
2417 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
2418 a2d2e1a7 Iustin Pop
    @rtype: list
2419 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
2420 a2d2e1a7 Iustin Pop

2421 a2d2e1a7 Iustin Pop
    """
2422 a2d2e1a7 Iustin Pop
    return [val for val in items if not self.Matches(val)]