Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 2034c70d

History | View | Annotate | Download (99.5 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 c1dd99d4 Michael Hanselmann
import sys
32 a8083063 Iustin Pop
import time
33 113b55aa Iustin Pop
import subprocess
34 a8083063 Iustin Pop
import re
35 a8083063 Iustin Pop
import socket
36 a8083063 Iustin Pop
import tempfile
37 a8083063 Iustin Pop
import shutil
38 4ca1b175 Alexander Schreiber
import errno
39 2f8b60b3 Iustin Pop
import pwd
40 78feb6fb Guido Trotter
import itertools
41 9c233417 Iustin Pop
import select
42 9c233417 Iustin Pop
import fcntl
43 8f765069 Iustin Pop
import resource
44 bb698c1f Iustin Pop
import logging
45 551b6283 Iustin Pop
import logging.handlers
46 de499029 Michael Hanselmann
import signal
47 bdd5e420 Michael Hanselmann
import OpenSSL
48 27e46076 Michael Hanselmann
import datetime
49 27e46076 Michael Hanselmann
import calendar
50 68857643 Michael Hanselmann
import hmac
51 339be5a8 Michael Hanselmann
import collections
52 9c233417 Iustin Pop
53 9c233417 Iustin Pop
from cStringIO import StringIO
54 a8083063 Iustin Pop
55 7ffe8fba Carlos Valiente
try:
56 23e0ef8c Guido Trotter
  # pylint: disable-msg=F0401
57 4b6fa0bf Luca Bigliardi
  import ctypes
58 4b6fa0bf Luca Bigliardi
except ImportError:
59 4b6fa0bf Luca Bigliardi
  ctypes = None
60 4b6fa0bf Luca Bigliardi
61 a8083063 Iustin Pop
from ganeti import errors
62 3aecd2c7 Iustin Pop
from ganeti import constants
63 716a32cb Guido Trotter
from ganeti import compat
64 a744b676 Manuel Franceschini
from ganeti import netutils
65 a8083063 Iustin Pop
66 16abfbc2 Alexander Schreiber
67 a8083063 Iustin Pop
_locksheld = []
68 a8083063 Iustin Pop
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
69 a8083063 Iustin Pop
70 e67bd559 Michael Hanselmann
debug_locks = False
71 58885d79 Iustin Pop
72 58885d79 Iustin Pop
#: when set to True, L{RunCmd} is disabled
73 b74159ee Iustin Pop
no_fork = False
74 f362096f Iustin Pop
75 13998ef2 Michael Hanselmann
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
76 13998ef2 Michael Hanselmann
77 68857643 Michael Hanselmann
HEX_CHAR_RE = r"[a-zA-Z0-9]"
78 68857643 Michael Hanselmann
VALID_X509_SIGNATURE_SALT = re.compile("^%s+$" % HEX_CHAR_RE, re.S)
79 68857643 Michael Hanselmann
X509_SIGNATURE = re.compile(r"^%s:\s*(?P<salt>%s+)/(?P<sign>%s+)$" %
80 68857643 Michael Hanselmann
                            (re.escape(constants.X509_CERT_SIGNATURE_HEADER),
81 68857643 Michael Hanselmann
                             HEX_CHAR_RE, HEX_CHAR_RE),
82 68857643 Michael Hanselmann
                            re.S | re.I)
83 68857643 Michael Hanselmann
84 28f34048 Michael Hanselmann
_VALID_SERVICE_NAME_RE = re.compile("^[-_.a-zA-Z0-9]{1,128}$")
85 28f34048 Michael Hanselmann
86 24d70417 Michael Hanselmann
# Certificate verification results
87 24d70417 Michael Hanselmann
(CERT_WARNING,
88 24d70417 Michael Hanselmann
 CERT_ERROR) = range(1, 3)
89 24d70417 Michael Hanselmann
90 4b6fa0bf Luca Bigliardi
# Flags for mlockall() (from bits/mman.h)
91 4b6fa0bf Luca Bigliardi
_MCL_CURRENT = 1
92 4b6fa0bf Luca Bigliardi
_MCL_FUTURE = 2
93 4b6fa0bf Luca Bigliardi
94 7c0d6283 Michael Hanselmann
95 a8083063 Iustin Pop
class RunResult(object):
96 58885d79 Iustin Pop
  """Holds the result of running external programs.
97 58885d79 Iustin Pop

98 58885d79 Iustin Pop
  @type exit_code: int
99 58885d79 Iustin Pop
  @ivar exit_code: the exit code of the program, or None (if the program
100 58885d79 Iustin Pop
      didn't exit())
101 58885d79 Iustin Pop
  @type signal: int or None
102 58885d79 Iustin Pop
  @ivar signal: the signal that caused the program to finish, or None
103 58885d79 Iustin Pop
      (if the program wasn't terminated by a signal)
104 58885d79 Iustin Pop
  @type stdout: str
105 58885d79 Iustin Pop
  @ivar stdout: the standard output of the program
106 58885d79 Iustin Pop
  @type stderr: str
107 58885d79 Iustin Pop
  @ivar stderr: the standard error of the program
108 58885d79 Iustin Pop
  @type failed: boolean
109 58885d79 Iustin Pop
  @ivar failed: True in case the program was
110 58885d79 Iustin Pop
      terminated by a signal or exited with a non-zero exit code
111 58885d79 Iustin Pop
  @ivar fail_reason: a string detailing the termination reason
112 a8083063 Iustin Pop

113 a8083063 Iustin Pop
  """
114 a8083063 Iustin Pop
  __slots__ = ["exit_code", "signal", "stdout", "stderr",
115 a8083063 Iustin Pop
               "failed", "fail_reason", "cmd"]
116 a8083063 Iustin Pop
117 a8083063 Iustin Pop
118 38206f3c Iustin Pop
  def __init__(self, exit_code, signal_, stdout, stderr, cmd):
119 a8083063 Iustin Pop
    self.cmd = cmd
120 a8083063 Iustin Pop
    self.exit_code = exit_code
121 38206f3c Iustin Pop
    self.signal = signal_
122 a8083063 Iustin Pop
    self.stdout = stdout
123 a8083063 Iustin Pop
    self.stderr = stderr
124 38206f3c Iustin Pop
    self.failed = (signal_ is not None or exit_code != 0)
125 a8083063 Iustin Pop
126 a8083063 Iustin Pop
    if self.signal is not None:
127 a8083063 Iustin Pop
      self.fail_reason = "terminated by signal %s" % self.signal
128 a8083063 Iustin Pop
    elif self.exit_code is not None:
129 a8083063 Iustin Pop
      self.fail_reason = "exited with exit code %s" % self.exit_code
130 a8083063 Iustin Pop
    else:
131 a8083063 Iustin Pop
      self.fail_reason = "unable to determine termination reason"
132 a8083063 Iustin Pop
133 bb698c1f Iustin Pop
    if self.failed:
134 bb698c1f Iustin Pop
      logging.debug("Command '%s' failed (%s); output: %s",
135 bb698c1f Iustin Pop
                    self.cmd, self.fail_reason, self.output)
136 f362096f Iustin Pop
137 a8083063 Iustin Pop
  def _GetOutput(self):
138 a8083063 Iustin Pop
    """Returns the combined stdout and stderr for easier usage.
139 a8083063 Iustin Pop

140 a8083063 Iustin Pop
    """
141 a8083063 Iustin Pop
    return self.stdout + self.stderr
142 a8083063 Iustin Pop
143 a8083063 Iustin Pop
  output = property(_GetOutput, None, None, "Return full output")
144 a8083063 Iustin Pop
145 a8083063 Iustin Pop
146 bb3776b4 Michael Hanselmann
def _BuildCmdEnvironment(env, reset):
147 c1dd99d4 Michael Hanselmann
  """Builds the environment for an external program.
148 c1dd99d4 Michael Hanselmann

149 c1dd99d4 Michael Hanselmann
  """
150 bb3776b4 Michael Hanselmann
  if reset:
151 bb3776b4 Michael Hanselmann
    cmd_env = {}
152 bb3776b4 Michael Hanselmann
  else:
153 bb3776b4 Michael Hanselmann
    cmd_env = os.environ.copy()
154 bb3776b4 Michael Hanselmann
    cmd_env["LC_ALL"] = "C"
155 bb3776b4 Michael Hanselmann
156 c1dd99d4 Michael Hanselmann
  if env is not None:
157 c1dd99d4 Michael Hanselmann
    cmd_env.update(env)
158 bb3776b4 Michael Hanselmann
159 c1dd99d4 Michael Hanselmann
  return cmd_env
160 c1dd99d4 Michael Hanselmann
161 c1dd99d4 Michael Hanselmann
162 bb3776b4 Michael Hanselmann
def RunCmd(cmd, env=None, output=None, cwd="/", reset_env=False):
163 a8083063 Iustin Pop
  """Execute a (shell) command.
164 a8083063 Iustin Pop

165 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
166 a8083063 Iustin Pop
  closed.
167 a8083063 Iustin Pop

168 c1dd99d4 Michael Hanselmann
  @type cmd: string or list
169 36117c2b Iustin Pop
  @param cmd: Command to run
170 2557ff82 Guido Trotter
  @type env: dict
171 c1dd99d4 Michael Hanselmann
  @param env: Additional environment variables
172 36117c2b Iustin Pop
  @type output: str
173 58885d79 Iustin Pop
  @param output: if desired, the output of the command can be
174 36117c2b Iustin Pop
      saved in a file instead of the RunResult instance; this
175 36117c2b Iustin Pop
      parameter denotes the file name (if not None)
176 8797df43 Iustin Pop
  @type cwd: string
177 8797df43 Iustin Pop
  @param cwd: if specified, will be used as the working
178 8797df43 Iustin Pop
      directory for the command; the default will be /
179 bf4daac9 Guido Trotter
  @type reset_env: boolean
180 bf4daac9 Guido Trotter
  @param reset_env: whether to reset or keep the default os environment
181 36117c2b Iustin Pop
  @rtype: L{RunResult}
182 58885d79 Iustin Pop
  @return: RunResult instance
183 5bbd3f7f Michael Hanselmann
  @raise errors.ProgrammerError: if we call this when forks are disabled
184 a8083063 Iustin Pop

185 a8083063 Iustin Pop
  """
186 b74159ee Iustin Pop
  if no_fork:
187 b74159ee Iustin Pop
    raise errors.ProgrammerError("utils.RunCmd() called with fork() disabled")
188 b74159ee Iustin Pop
189 c1dd99d4 Michael Hanselmann
  if isinstance(cmd, basestring):
190 c1dd99d4 Michael Hanselmann
    strcmd = cmd
191 c1dd99d4 Michael Hanselmann
    shell = True
192 c1dd99d4 Michael Hanselmann
  else:
193 a8083063 Iustin Pop
    cmd = [str(val) for val in cmd]
194 c1dd99d4 Michael Hanselmann
    strcmd = ShellQuoteArgs(cmd)
195 113b55aa Iustin Pop
    shell = False
196 c1dd99d4 Michael Hanselmann
197 c1dd99d4 Michael Hanselmann
  if output:
198 c1dd99d4 Michael Hanselmann
    logging.debug("RunCmd %s, output file '%s'", strcmd, output)
199 113b55aa Iustin Pop
  else:
200 c1dd99d4 Michael Hanselmann
    logging.debug("RunCmd %s", strcmd)
201 2557ff82 Guido Trotter
202 bb3776b4 Michael Hanselmann
  cmd_env = _BuildCmdEnvironment(env, reset_env)
203 2557ff82 Guido Trotter
204 c803b052 Iustin Pop
  try:
205 c803b052 Iustin Pop
    if output is None:
206 c803b052 Iustin Pop
      out, err, status = _RunCmdPipe(cmd, cmd_env, shell, cwd)
207 c803b052 Iustin Pop
    else:
208 c803b052 Iustin Pop
      status = _RunCmdFile(cmd, cmd_env, shell, output, cwd)
209 c803b052 Iustin Pop
      out = err = ""
210 c803b052 Iustin Pop
  except OSError, err:
211 c803b052 Iustin Pop
    if err.errno == errno.ENOENT:
212 c803b052 Iustin Pop
      raise errors.OpExecError("Can't execute '%s': not found (%s)" %
213 c803b052 Iustin Pop
                               (strcmd, err))
214 c803b052 Iustin Pop
    else:
215 c803b052 Iustin Pop
      raise
216 36117c2b Iustin Pop
217 36117c2b Iustin Pop
  if status >= 0:
218 36117c2b Iustin Pop
    exitcode = status
219 36117c2b Iustin Pop
    signal_ = None
220 36117c2b Iustin Pop
  else:
221 36117c2b Iustin Pop
    exitcode = None
222 36117c2b Iustin Pop
    signal_ = -status
223 36117c2b Iustin Pop
224 36117c2b Iustin Pop
  return RunResult(exitcode, signal_, out, err, strcmd)
225 36117c2b Iustin Pop
226 ae59efea Michael Hanselmann
227 c1dd99d4 Michael Hanselmann
def StartDaemon(cmd, env=None, cwd="/", output=None, output_fd=None,
228 c1dd99d4 Michael Hanselmann
                pidfile=None):
229 c1dd99d4 Michael Hanselmann
  """Start a daemon process after forking twice.
230 c1dd99d4 Michael Hanselmann

231 c1dd99d4 Michael Hanselmann
  @type cmd: string or list
232 c1dd99d4 Michael Hanselmann
  @param cmd: Command to run
233 c1dd99d4 Michael Hanselmann
  @type env: dict
234 c1dd99d4 Michael Hanselmann
  @param env: Additional environment variables
235 c1dd99d4 Michael Hanselmann
  @type cwd: string
236 c1dd99d4 Michael Hanselmann
  @param cwd: Working directory for the program
237 c1dd99d4 Michael Hanselmann
  @type output: string
238 c1dd99d4 Michael Hanselmann
  @param output: Path to file in which to save the output
239 c1dd99d4 Michael Hanselmann
  @type output_fd: int
240 c1dd99d4 Michael Hanselmann
  @param output_fd: File descriptor for output
241 c1dd99d4 Michael Hanselmann
  @type pidfile: string
242 c1dd99d4 Michael Hanselmann
  @param pidfile: Process ID file
243 c1dd99d4 Michael Hanselmann
  @rtype: int
244 c1dd99d4 Michael Hanselmann
  @return: Daemon process ID
245 c1dd99d4 Michael Hanselmann
  @raise errors.ProgrammerError: if we call this when forks are disabled
246 c1dd99d4 Michael Hanselmann

247 c1dd99d4 Michael Hanselmann
  """
248 c1dd99d4 Michael Hanselmann
  if no_fork:
249 c1dd99d4 Michael Hanselmann
    raise errors.ProgrammerError("utils.StartDaemon() called with fork()"
250 c1dd99d4 Michael Hanselmann
                                 " disabled")
251 c1dd99d4 Michael Hanselmann
252 c1dd99d4 Michael Hanselmann
  if output and not (bool(output) ^ (output_fd is not None)):
253 c1dd99d4 Michael Hanselmann
    raise errors.ProgrammerError("Only one of 'output' and 'output_fd' can be"
254 c1dd99d4 Michael Hanselmann
                                 " specified")
255 c1dd99d4 Michael Hanselmann
256 c1dd99d4 Michael Hanselmann
  if isinstance(cmd, basestring):
257 c1dd99d4 Michael Hanselmann
    cmd = ["/bin/sh", "-c", cmd]
258 c1dd99d4 Michael Hanselmann
259 c1dd99d4 Michael Hanselmann
  strcmd = ShellQuoteArgs(cmd)
260 c1dd99d4 Michael Hanselmann
261 c1dd99d4 Michael Hanselmann
  if output:
262 c1dd99d4 Michael Hanselmann
    logging.debug("StartDaemon %s, output file '%s'", strcmd, output)
263 c1dd99d4 Michael Hanselmann
  else:
264 c1dd99d4 Michael Hanselmann
    logging.debug("StartDaemon %s", strcmd)
265 c1dd99d4 Michael Hanselmann
266 bb3776b4 Michael Hanselmann
  cmd_env = _BuildCmdEnvironment(env, False)
267 c1dd99d4 Michael Hanselmann
268 c1dd99d4 Michael Hanselmann
  # Create pipe for sending PID back
269 c1dd99d4 Michael Hanselmann
  (pidpipe_read, pidpipe_write) = os.pipe()
270 c1dd99d4 Michael Hanselmann
  try:
271 c1dd99d4 Michael Hanselmann
    try:
272 c1dd99d4 Michael Hanselmann
      # Create pipe for sending error messages
273 c1dd99d4 Michael Hanselmann
      (errpipe_read, errpipe_write) = os.pipe()
274 c1dd99d4 Michael Hanselmann
      try:
275 c1dd99d4 Michael Hanselmann
        try:
276 c1dd99d4 Michael Hanselmann
          # First fork
277 c1dd99d4 Michael Hanselmann
          pid = os.fork()
278 c1dd99d4 Michael Hanselmann
          if pid == 0:
279 c1dd99d4 Michael Hanselmann
            try:
280 c1dd99d4 Michael Hanselmann
              # Child process, won't return
281 e0bb431e Michael Hanselmann
              _StartDaemonChild(errpipe_read, errpipe_write,
282 e0bb431e Michael Hanselmann
                                pidpipe_read, pidpipe_write,
283 e0bb431e Michael Hanselmann
                                cmd, cmd_env, cwd,
284 e0bb431e Michael Hanselmann
                                output, output_fd, pidfile)
285 c1dd99d4 Michael Hanselmann
            finally:
286 c1dd99d4 Michael Hanselmann
              # Well, maybe child process failed
287 e0bb431e Michael Hanselmann
              os._exit(1) # pylint: disable-msg=W0212
288 c1dd99d4 Michael Hanselmann
        finally:
289 c1dd99d4 Michael Hanselmann
          _CloseFDNoErr(errpipe_write)
290 c1dd99d4 Michael Hanselmann
291 c1dd99d4 Michael Hanselmann
        # Wait for daemon to be started (or an error message to arrive) and read
292 c1dd99d4 Michael Hanselmann
        # up to 100 KB as an error message
293 c1dd99d4 Michael Hanselmann
        errormsg = RetryOnSignal(os.read, errpipe_read, 100 * 1024)
294 c1dd99d4 Michael Hanselmann
      finally:
295 c1dd99d4 Michael Hanselmann
        _CloseFDNoErr(errpipe_read)
296 c1dd99d4 Michael Hanselmann
    finally:
297 c1dd99d4 Michael Hanselmann
      _CloseFDNoErr(pidpipe_write)
298 c1dd99d4 Michael Hanselmann
299 c1dd99d4 Michael Hanselmann
    # Read up to 128 bytes for PID
300 c1dd99d4 Michael Hanselmann
    pidtext = RetryOnSignal(os.read, pidpipe_read, 128)
301 c1dd99d4 Michael Hanselmann
  finally:
302 c1dd99d4 Michael Hanselmann
    _CloseFDNoErr(pidpipe_read)
303 c1dd99d4 Michael Hanselmann
304 c1dd99d4 Michael Hanselmann
  # Try to avoid zombies by waiting for child process
305 c1dd99d4 Michael Hanselmann
  try:
306 c1dd99d4 Michael Hanselmann
    os.waitpid(pid, 0)
307 c1dd99d4 Michael Hanselmann
  except OSError:
308 c1dd99d4 Michael Hanselmann
    pass
309 c1dd99d4 Michael Hanselmann
310 c1dd99d4 Michael Hanselmann
  if errormsg:
311 c1dd99d4 Michael Hanselmann
    raise errors.OpExecError("Error when starting daemon process: %r" %
312 c1dd99d4 Michael Hanselmann
                             errormsg)
313 c1dd99d4 Michael Hanselmann
314 c1dd99d4 Michael Hanselmann
  try:
315 c1dd99d4 Michael Hanselmann
    return int(pidtext)
316 c1dd99d4 Michael Hanselmann
  except (ValueError, TypeError), err:
317 c1dd99d4 Michael Hanselmann
    raise errors.OpExecError("Error while trying to parse PID %r: %s" %
318 c1dd99d4 Michael Hanselmann
                             (pidtext, err))
319 c1dd99d4 Michael Hanselmann
320 c1dd99d4 Michael Hanselmann
321 e0bb431e Michael Hanselmann
def _StartDaemonChild(errpipe_read, errpipe_write,
322 e0bb431e Michael Hanselmann
                      pidpipe_read, pidpipe_write,
323 e0bb431e Michael Hanselmann
                      args, env, cwd,
324 e0bb431e Michael Hanselmann
                      output, fd_output, pidfile):
325 c1dd99d4 Michael Hanselmann
  """Child process for starting daemon.
326 c1dd99d4 Michael Hanselmann

327 c1dd99d4 Michael Hanselmann
  """
328 c1dd99d4 Michael Hanselmann
  try:
329 c1dd99d4 Michael Hanselmann
    # Close parent's side
330 c1dd99d4 Michael Hanselmann
    _CloseFDNoErr(errpipe_read)
331 c1dd99d4 Michael Hanselmann
    _CloseFDNoErr(pidpipe_read)
332 c1dd99d4 Michael Hanselmann
333 c1dd99d4 Michael Hanselmann
    # First child process
334 c1dd99d4 Michael Hanselmann
    os.chdir("/")
335 c1dd99d4 Michael Hanselmann
    os.umask(077)
336 c1dd99d4 Michael Hanselmann
    os.setsid()
337 c1dd99d4 Michael Hanselmann
338 c1dd99d4 Michael Hanselmann
    # And fork for the second time
339 c1dd99d4 Michael Hanselmann
    pid = os.fork()
340 c1dd99d4 Michael Hanselmann
    if pid != 0:
341 c1dd99d4 Michael Hanselmann
      # Exit first child process
342 c1dd99d4 Michael Hanselmann
      os._exit(0) # pylint: disable-msg=W0212
343 c1dd99d4 Michael Hanselmann
344 c1dd99d4 Michael Hanselmann
    # Make sure pipe is closed on execv* (and thereby notifies original process)
345 c1dd99d4 Michael Hanselmann
    SetCloseOnExecFlag(errpipe_write, True)
346 c1dd99d4 Michael Hanselmann
347 c1dd99d4 Michael Hanselmann
    # List of file descriptors to be left open
348 c1dd99d4 Michael Hanselmann
    noclose_fds = [errpipe_write]
349 c1dd99d4 Michael Hanselmann
350 c1dd99d4 Michael Hanselmann
    # Open PID file
351 c1dd99d4 Michael Hanselmann
    if pidfile:
352 c1dd99d4 Michael Hanselmann
      try:
353 c1dd99d4 Michael Hanselmann
        # TODO: Atomic replace with another locked file instead of writing into
354 c1dd99d4 Michael Hanselmann
        # it after creating
355 c1dd99d4 Michael Hanselmann
        fd_pidfile = os.open(pidfile, os.O_WRONLY | os.O_CREAT, 0600)
356 c1dd99d4 Michael Hanselmann
357 c1dd99d4 Michael Hanselmann
        # Lock the PID file (and fail if not possible to do so). Any code
358 c1dd99d4 Michael Hanselmann
        # wanting to send a signal to the daemon should try to lock the PID
359 c1dd99d4 Michael Hanselmann
        # file before reading it. If acquiring the lock succeeds, the daemon is
360 c1dd99d4 Michael Hanselmann
        # no longer running and the signal should not be sent.
361 c1dd99d4 Michael Hanselmann
        LockFile(fd_pidfile)
362 c1dd99d4 Michael Hanselmann
363 c1dd99d4 Michael Hanselmann
        os.write(fd_pidfile, "%d\n" % os.getpid())
364 c1dd99d4 Michael Hanselmann
      except Exception, err:
365 c1dd99d4 Michael Hanselmann
        raise Exception("Creating and locking PID file failed: %s" % err)
366 c1dd99d4 Michael Hanselmann
367 c1dd99d4 Michael Hanselmann
      # Keeping the file open to hold the lock
368 c1dd99d4 Michael Hanselmann
      noclose_fds.append(fd_pidfile)
369 c1dd99d4 Michael Hanselmann
370 c1dd99d4 Michael Hanselmann
      SetCloseOnExecFlag(fd_pidfile, False)
371 c1dd99d4 Michael Hanselmann
    else:
372 c1dd99d4 Michael Hanselmann
      fd_pidfile = None
373 c1dd99d4 Michael Hanselmann
374 c1dd99d4 Michael Hanselmann
    # Open /dev/null
375 c1dd99d4 Michael Hanselmann
    fd_devnull = os.open(os.devnull, os.O_RDWR)
376 c1dd99d4 Michael Hanselmann
377 c1dd99d4 Michael Hanselmann
    assert not output or (bool(output) ^ (fd_output is not None))
378 c1dd99d4 Michael Hanselmann
379 c1dd99d4 Michael Hanselmann
    if fd_output is not None:
380 c1dd99d4 Michael Hanselmann
      pass
381 c1dd99d4 Michael Hanselmann
    elif output:
382 c1dd99d4 Michael Hanselmann
      # Open output file
383 c1dd99d4 Michael Hanselmann
      try:
384 c1dd99d4 Michael Hanselmann
        # TODO: Implement flag to set append=yes/no
385 c1dd99d4 Michael Hanselmann
        fd_output = os.open(output, os.O_WRONLY | os.O_CREAT, 0600)
386 c1dd99d4 Michael Hanselmann
      except EnvironmentError, err:
387 c1dd99d4 Michael Hanselmann
        raise Exception("Opening output file failed: %s" % err)
388 c1dd99d4 Michael Hanselmann
    else:
389 c1dd99d4 Michael Hanselmann
      fd_output = fd_devnull
390 c1dd99d4 Michael Hanselmann
391 c1dd99d4 Michael Hanselmann
    # Redirect standard I/O
392 c1dd99d4 Michael Hanselmann
    os.dup2(fd_devnull, 0)
393 c1dd99d4 Michael Hanselmann
    os.dup2(fd_output, 1)
394 c1dd99d4 Michael Hanselmann
    os.dup2(fd_output, 2)
395 c1dd99d4 Michael Hanselmann
396 c1dd99d4 Michael Hanselmann
    # Send daemon PID to parent
397 c1dd99d4 Michael Hanselmann
    RetryOnSignal(os.write, pidpipe_write, str(os.getpid()))
398 c1dd99d4 Michael Hanselmann
399 c1dd99d4 Michael Hanselmann
    # Close all file descriptors except stdio and error message pipe
400 c1dd99d4 Michael Hanselmann
    CloseFDs(noclose_fds=noclose_fds)
401 c1dd99d4 Michael Hanselmann
402 c1dd99d4 Michael Hanselmann
    # Change working directory
403 c1dd99d4 Michael Hanselmann
    os.chdir(cwd)
404 c1dd99d4 Michael Hanselmann
405 c1dd99d4 Michael Hanselmann
    if env is None:
406 c1dd99d4 Michael Hanselmann
      os.execvp(args[0], args)
407 c1dd99d4 Michael Hanselmann
    else:
408 c1dd99d4 Michael Hanselmann
      os.execvpe(args[0], args, env)
409 c1dd99d4 Michael Hanselmann
  except: # pylint: disable-msg=W0702
410 c1dd99d4 Michael Hanselmann
    try:
411 c1dd99d4 Michael Hanselmann
      # Report errors to original process
412 c1dd99d4 Michael Hanselmann
      buf = str(sys.exc_info()[1])
413 c1dd99d4 Michael Hanselmann
414 c1dd99d4 Michael Hanselmann
      RetryOnSignal(os.write, errpipe_write, buf)
415 c1dd99d4 Michael Hanselmann
    except: # pylint: disable-msg=W0702
416 c1dd99d4 Michael Hanselmann
      # Ignore errors in error handling
417 c1dd99d4 Michael Hanselmann
      pass
418 c1dd99d4 Michael Hanselmann
419 c1dd99d4 Michael Hanselmann
  os._exit(1) # pylint: disable-msg=W0212
420 c1dd99d4 Michael Hanselmann
421 c1dd99d4 Michael Hanselmann
422 8797df43 Iustin Pop
def _RunCmdPipe(cmd, env, via_shell, cwd):
423 36117c2b Iustin Pop
  """Run a command and return its output.
424 36117c2b Iustin Pop

425 36117c2b Iustin Pop
  @type  cmd: string or list
426 36117c2b Iustin Pop
  @param cmd: Command to run
427 36117c2b Iustin Pop
  @type env: dict
428 36117c2b Iustin Pop
  @param env: The environment to use
429 36117c2b Iustin Pop
  @type via_shell: bool
430 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
431 8797df43 Iustin Pop
  @type cwd: string
432 8797df43 Iustin Pop
  @param cwd: the working directory for the program
433 36117c2b Iustin Pop
  @rtype: tuple
434 36117c2b Iustin Pop
  @return: (out, err, status)
435 36117c2b Iustin Pop

436 36117c2b Iustin Pop
  """
437 9c233417 Iustin Pop
  poller = select.poll()
438 36117c2b Iustin Pop
  child = subprocess.Popen(cmd, shell=via_shell,
439 113b55aa Iustin Pop
                           stderr=subprocess.PIPE,
440 113b55aa Iustin Pop
                           stdout=subprocess.PIPE,
441 113b55aa Iustin Pop
                           stdin=subprocess.PIPE,
442 8797df43 Iustin Pop
                           close_fds=True, env=env,
443 8797df43 Iustin Pop
                           cwd=cwd)
444 113b55aa Iustin Pop
445 113b55aa Iustin Pop
  child.stdin.close()
446 9c233417 Iustin Pop
  poller.register(child.stdout, select.POLLIN)
447 9c233417 Iustin Pop
  poller.register(child.stderr, select.POLLIN)
448 9c233417 Iustin Pop
  out = StringIO()
449 9c233417 Iustin Pop
  err = StringIO()
450 9c233417 Iustin Pop
  fdmap = {
451 9c233417 Iustin Pop
    child.stdout.fileno(): (out, child.stdout),
452 9c233417 Iustin Pop
    child.stderr.fileno(): (err, child.stderr),
453 9c233417 Iustin Pop
    }
454 9c233417 Iustin Pop
  for fd in fdmap:
455 287a1740 Michael Hanselmann
    SetNonblockFlag(fd, True)
456 9c233417 Iustin Pop
457 9c233417 Iustin Pop
  while fdmap:
458 edcb5d9e Michael Hanselmann
    pollresult = RetryOnSignal(poller.poll)
459 bf988c29 Guido Trotter
460 bf988c29 Guido Trotter
    for fd, event in pollresult:
461 9c233417 Iustin Pop
      if event & select.POLLIN or event & select.POLLPRI:
462 9c233417 Iustin Pop
        data = fdmap[fd][1].read()
463 9c233417 Iustin Pop
        # no data from read signifies EOF (the same as POLLHUP)
464 9c233417 Iustin Pop
        if not data:
465 9c233417 Iustin Pop
          poller.unregister(fd)
466 9c233417 Iustin Pop
          del fdmap[fd]
467 9c233417 Iustin Pop
          continue
468 9c233417 Iustin Pop
        fdmap[fd][0].write(data)
469 9c233417 Iustin Pop
      if (event & select.POLLNVAL or event & select.POLLHUP or
470 9c233417 Iustin Pop
          event & select.POLLERR):
471 9c233417 Iustin Pop
        poller.unregister(fd)
472 9c233417 Iustin Pop
        del fdmap[fd]
473 9c233417 Iustin Pop
474 9c233417 Iustin Pop
  out = out.getvalue()
475 9c233417 Iustin Pop
  err = err.getvalue()
476 a8083063 Iustin Pop
477 a8083063 Iustin Pop
  status = child.wait()
478 36117c2b Iustin Pop
  return out, err, status
479 a8083063 Iustin Pop
480 36117c2b Iustin Pop
481 8797df43 Iustin Pop
def _RunCmdFile(cmd, env, via_shell, output, cwd):
482 36117c2b Iustin Pop
  """Run a command and save its output to a file.
483 36117c2b Iustin Pop

484 36117c2b Iustin Pop
  @type  cmd: string or list
485 36117c2b Iustin Pop
  @param cmd: Command to run
486 36117c2b Iustin Pop
  @type env: dict
487 36117c2b Iustin Pop
  @param env: The environment to use
488 36117c2b Iustin Pop
  @type via_shell: bool
489 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
490 36117c2b Iustin Pop
  @type output: str
491 36117c2b Iustin Pop
  @param output: the filename in which to save the output
492 8797df43 Iustin Pop
  @type cwd: string
493 8797df43 Iustin Pop
  @param cwd: the working directory for the program
494 36117c2b Iustin Pop
  @rtype: int
495 36117c2b Iustin Pop
  @return: the exit status
496 36117c2b Iustin Pop

497 36117c2b Iustin Pop
  """
498 36117c2b Iustin Pop
  fh = open(output, "a")
499 36117c2b Iustin Pop
  try:
500 36117c2b Iustin Pop
    child = subprocess.Popen(cmd, shell=via_shell,
501 36117c2b Iustin Pop
                             stderr=subprocess.STDOUT,
502 36117c2b Iustin Pop
                             stdout=fh,
503 36117c2b Iustin Pop
                             stdin=subprocess.PIPE,
504 8797df43 Iustin Pop
                             close_fds=True, env=env,
505 8797df43 Iustin Pop
                             cwd=cwd)
506 36117c2b Iustin Pop
507 36117c2b Iustin Pop
    child.stdin.close()
508 36117c2b Iustin Pop
    status = child.wait()
509 36117c2b Iustin Pop
  finally:
510 36117c2b Iustin Pop
    fh.close()
511 36117c2b Iustin Pop
  return status
512 a8083063 Iustin Pop
513 a8083063 Iustin Pop
514 73027ed2 Michael Hanselmann
def SetCloseOnExecFlag(fd, enable):
515 73027ed2 Michael Hanselmann
  """Sets or unsets the close-on-exec flag on a file descriptor.
516 73027ed2 Michael Hanselmann

517 73027ed2 Michael Hanselmann
  @type fd: int
518 73027ed2 Michael Hanselmann
  @param fd: File descriptor
519 73027ed2 Michael Hanselmann
  @type enable: bool
520 73027ed2 Michael Hanselmann
  @param enable: Whether to set or unset it.
521 73027ed2 Michael Hanselmann

522 73027ed2 Michael Hanselmann
  """
523 73027ed2 Michael Hanselmann
  flags = fcntl.fcntl(fd, fcntl.F_GETFD)
524 73027ed2 Michael Hanselmann
525 73027ed2 Michael Hanselmann
  if enable:
526 73027ed2 Michael Hanselmann
    flags |= fcntl.FD_CLOEXEC
527 73027ed2 Michael Hanselmann
  else:
528 73027ed2 Michael Hanselmann
    flags &= ~fcntl.FD_CLOEXEC
529 73027ed2 Michael Hanselmann
530 73027ed2 Michael Hanselmann
  fcntl.fcntl(fd, fcntl.F_SETFD, flags)
531 73027ed2 Michael Hanselmann
532 73027ed2 Michael Hanselmann
533 287a1740 Michael Hanselmann
def SetNonblockFlag(fd, enable):
534 287a1740 Michael Hanselmann
  """Sets or unsets the O_NONBLOCK flag on on a file descriptor.
535 287a1740 Michael Hanselmann

536 287a1740 Michael Hanselmann
  @type fd: int
537 287a1740 Michael Hanselmann
  @param fd: File descriptor
538 287a1740 Michael Hanselmann
  @type enable: bool
539 287a1740 Michael Hanselmann
  @param enable: Whether to set or unset it
540 287a1740 Michael Hanselmann

541 287a1740 Michael Hanselmann
  """
542 287a1740 Michael Hanselmann
  flags = fcntl.fcntl(fd, fcntl.F_GETFL)
543 287a1740 Michael Hanselmann
544 287a1740 Michael Hanselmann
  if enable:
545 287a1740 Michael Hanselmann
    flags |= os.O_NONBLOCK
546 287a1740 Michael Hanselmann
  else:
547 287a1740 Michael Hanselmann
    flags &= ~os.O_NONBLOCK
548 287a1740 Michael Hanselmann
549 287a1740 Michael Hanselmann
  fcntl.fcntl(fd, fcntl.F_SETFL, flags)
550 287a1740 Michael Hanselmann
551 287a1740 Michael Hanselmann
552 edcb5d9e Michael Hanselmann
def RetryOnSignal(fn, *args, **kwargs):
553 edcb5d9e Michael Hanselmann
  """Calls a function again if it failed due to EINTR.
554 edcb5d9e Michael Hanselmann

555 edcb5d9e Michael Hanselmann
  """
556 edcb5d9e Michael Hanselmann
  while True:
557 edcb5d9e Michael Hanselmann
    try:
558 edcb5d9e Michael Hanselmann
      return fn(*args, **kwargs)
559 965d0e5b Guido Trotter
    except EnvironmentError, err:
560 edcb5d9e Michael Hanselmann
      if err.errno != errno.EINTR:
561 edcb5d9e Michael Hanselmann
        raise
562 965d0e5b Guido Trotter
    except (socket.error, select.error), err:
563 965d0e5b Guido Trotter
      # In python 2.6 and above select.error is an IOError, so it's handled
564 965d0e5b Guido Trotter
      # above, in 2.5 and below it's not, and it's handled here.
565 edcb5d9e Michael Hanselmann
      if not (err.args and err.args[0] == errno.EINTR):
566 edcb5d9e Michael Hanselmann
        raise
567 edcb5d9e Michael Hanselmann
568 edcb5d9e Michael Hanselmann
569 6bb65e3a Guido Trotter
def RunParts(dir_name, env=None, reset_env=False):
570 6bb65e3a Guido Trotter
  """Run Scripts or programs in a directory
571 6bb65e3a Guido Trotter

572 6bb65e3a Guido Trotter
  @type dir_name: string
573 6bb65e3a Guido Trotter
  @param dir_name: absolute path to a directory
574 6bb65e3a Guido Trotter
  @type env: dict
575 6bb65e3a Guido Trotter
  @param env: The environment to use
576 6bb65e3a Guido Trotter
  @type reset_env: boolean
577 6bb65e3a Guido Trotter
  @param reset_env: whether to reset or keep the default os environment
578 6bb65e3a Guido Trotter
  @rtype: list of tuples
579 6bb65e3a Guido Trotter
  @return: list of (name, (one of RUNDIR_STATUS), RunResult)
580 6bb65e3a Guido Trotter

581 6bb65e3a Guido Trotter
  """
582 6bb65e3a Guido Trotter
  rr = []
583 6bb65e3a Guido Trotter
584 6bb65e3a Guido Trotter
  try:
585 6bb65e3a Guido Trotter
    dir_contents = ListVisibleFiles(dir_name)
586 6bb65e3a Guido Trotter
  except OSError, err:
587 6bb65e3a Guido Trotter
    logging.warning("RunParts: skipping %s (cannot list: %s)", dir_name, err)
588 6bb65e3a Guido Trotter
    return rr
589 6bb65e3a Guido Trotter
590 6bb65e3a Guido Trotter
  for relname in sorted(dir_contents):
591 c4feafe8 Iustin Pop
    fname = PathJoin(dir_name, relname)
592 6bb65e3a Guido Trotter
    if not (os.path.isfile(fname) and os.access(fname, os.X_OK) and
593 6bb65e3a Guido Trotter
            constants.EXT_PLUGIN_MASK.match(relname) is not None):
594 6bb65e3a Guido Trotter
      rr.append((relname, constants.RUNPARTS_SKIP, None))
595 6bb65e3a Guido Trotter
    else:
596 6bb65e3a Guido Trotter
      try:
597 6bb65e3a Guido Trotter
        result = RunCmd([fname], env=env, reset_env=reset_env)
598 6bb65e3a Guido Trotter
      except Exception, err: # pylint: disable-msg=W0703
599 6bb65e3a Guido Trotter
        rr.append((relname, constants.RUNPARTS_ERR, str(err)))
600 6bb65e3a Guido Trotter
      else:
601 6bb65e3a Guido Trotter
        rr.append((relname, constants.RUNPARTS_RUN, result))
602 6bb65e3a Guido Trotter
603 6bb65e3a Guido Trotter
  return rr
604 6bb65e3a Guido Trotter
605 6bb65e3a Guido Trotter
606 a8083063 Iustin Pop
def RemoveFile(filename):
607 a8083063 Iustin Pop
  """Remove a file ignoring some errors.
608 a8083063 Iustin Pop

609 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
610 a8083063 Iustin Pop
  errors are passed.
611 a8083063 Iustin Pop

612 58885d79 Iustin Pop
  @type filename: str
613 58885d79 Iustin Pop
  @param filename: the file to be removed
614 58885d79 Iustin Pop

615 a8083063 Iustin Pop
  """
616 a8083063 Iustin Pop
  try:
617 a8083063 Iustin Pop
    os.unlink(filename)
618 a8083063 Iustin Pop
  except OSError, err:
619 4ca1b175 Alexander Schreiber
    if err.errno not in (errno.ENOENT, errno.EISDIR):
620 a8083063 Iustin Pop
      raise
621 a8083063 Iustin Pop
622 72087dcd Balazs Lecz
623 72087dcd Balazs Lecz
def RemoveDir(dirname):
624 72087dcd Balazs Lecz
  """Remove an empty directory.
625 72087dcd Balazs Lecz

626 72087dcd Balazs Lecz
  Remove a directory, ignoring non-existing ones.
627 72087dcd Balazs Lecz
  Other errors are passed. This includes the case,
628 72087dcd Balazs Lecz
  where the directory is not empty, so it can't be removed.
629 72087dcd Balazs Lecz

630 72087dcd Balazs Lecz
  @type dirname: str
631 72087dcd Balazs Lecz
  @param dirname: the empty directory to be removed
632 72087dcd Balazs Lecz

633 72087dcd Balazs Lecz
  """
634 72087dcd Balazs Lecz
  try:
635 72087dcd Balazs Lecz
    os.rmdir(dirname)
636 72087dcd Balazs Lecz
  except OSError, err:
637 72087dcd Balazs Lecz
    if err.errno != errno.ENOENT:
638 72087dcd Balazs Lecz
      raise
639 72087dcd Balazs Lecz
640 a8083063 Iustin Pop
641 6e797216 Michael Hanselmann
def RenameFile(old, new, mkdir=False, mkdir_mode=0750):
642 6e797216 Michael Hanselmann
  """Renames a file.
643 6e797216 Michael Hanselmann

644 6e797216 Michael Hanselmann
  @type old: string
645 6e797216 Michael Hanselmann
  @param old: Original path
646 6e797216 Michael Hanselmann
  @type new: string
647 6e797216 Michael Hanselmann
  @param new: New path
648 6e797216 Michael Hanselmann
  @type mkdir: bool
649 6e797216 Michael Hanselmann
  @param mkdir: Whether to create target directory if it doesn't exist
650 6e797216 Michael Hanselmann
  @type mkdir_mode: int
651 6e797216 Michael Hanselmann
  @param mkdir_mode: Mode for newly created directories
652 6e797216 Michael Hanselmann

653 6e797216 Michael Hanselmann
  """
654 6e797216 Michael Hanselmann
  try:
655 6e797216 Michael Hanselmann
    return os.rename(old, new)
656 6e797216 Michael Hanselmann
  except OSError, err:
657 6e797216 Michael Hanselmann
    # In at least one use case of this function, the job queue, directory
658 6e797216 Michael Hanselmann
    # creation is very rare. Checking for the directory before renaming is not
659 6e797216 Michael Hanselmann
    # as efficient.
660 6e797216 Michael Hanselmann
    if mkdir and err.errno == errno.ENOENT:
661 6e797216 Michael Hanselmann
      # Create directory and try again
662 cc2f004d Michael Hanselmann
      Makedirs(os.path.dirname(new), mode=mkdir_mode)
663 a426508d Michael Hanselmann
664 6e797216 Michael Hanselmann
      return os.rename(old, new)
665 a426508d Michael Hanselmann
666 6e797216 Michael Hanselmann
    raise
667 6e797216 Michael Hanselmann
668 6e797216 Michael Hanselmann
669 76e5f8b5 Michael Hanselmann
def Makedirs(path, mode=0750):
670 76e5f8b5 Michael Hanselmann
  """Super-mkdir; create a leaf directory and all intermediate ones.
671 76e5f8b5 Michael Hanselmann

672 76e5f8b5 Michael Hanselmann
  This is a wrapper around C{os.makedirs} adding error handling not implemented
673 76e5f8b5 Michael Hanselmann
  before Python 2.5.
674 76e5f8b5 Michael Hanselmann

675 76e5f8b5 Michael Hanselmann
  """
676 76e5f8b5 Michael Hanselmann
  try:
677 76e5f8b5 Michael Hanselmann
    os.makedirs(path, mode)
678 76e5f8b5 Michael Hanselmann
  except OSError, err:
679 76e5f8b5 Michael Hanselmann
    # Ignore EEXIST. This is only handled in os.makedirs as included in
680 76e5f8b5 Michael Hanselmann
    # Python 2.5 and above.
681 76e5f8b5 Michael Hanselmann
    if err.errno != errno.EEXIST or not os.path.exists(path):
682 76e5f8b5 Michael Hanselmann
      raise
683 76e5f8b5 Michael Hanselmann
684 76e5f8b5 Michael Hanselmann
685 055f822b Michael Hanselmann
def ResetTempfileModule():
686 055f822b Michael Hanselmann
  """Resets the random name generator of the tempfile module.
687 055f822b Michael Hanselmann

688 055f822b Michael Hanselmann
  This function should be called after C{os.fork} in the child process to
689 055f822b Michael Hanselmann
  ensure it creates a newly seeded random generator. Otherwise it would
690 055f822b Michael Hanselmann
  generate the same random parts as the parent process. If several processes
691 055f822b Michael Hanselmann
  race for the creation of a temporary file, this could lead to one not getting
692 055f822b Michael Hanselmann
  a temporary name.
693 055f822b Michael Hanselmann

694 055f822b Michael Hanselmann
  """
695 055f822b Michael Hanselmann
  # pylint: disable-msg=W0212
696 055f822b Michael Hanselmann
  if hasattr(tempfile, "_once_lock") and hasattr(tempfile, "_name_sequence"):
697 055f822b Michael Hanselmann
    tempfile._once_lock.acquire()
698 055f822b Michael Hanselmann
    try:
699 055f822b Michael Hanselmann
      # Reset random name generator
700 055f822b Michael Hanselmann
      tempfile._name_sequence = None
701 055f822b Michael Hanselmann
    finally:
702 055f822b Michael Hanselmann
      tempfile._once_lock.release()
703 055f822b Michael Hanselmann
  else:
704 055f822b Michael Hanselmann
    logging.critical("The tempfile module misses at least one of the"
705 055f822b Michael Hanselmann
                     " '_once_lock' and '_name_sequence' attributes")
706 055f822b Michael Hanselmann
707 055f822b Michael Hanselmann
708 a8083063 Iustin Pop
def _FingerprintFile(filename):
709 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
710 a8083063 Iustin Pop

711 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
712 a8083063 Iustin Pop
  instead.
713 a8083063 Iustin Pop

714 58885d79 Iustin Pop
  @type filename: str
715 58885d79 Iustin Pop
  @param filename: the filename to checksum
716 58885d79 Iustin Pop
  @rtype: str
717 58885d79 Iustin Pop
  @return: the hex digest of the sha checksum of the contents
718 58885d79 Iustin Pop
      of the file
719 a8083063 Iustin Pop

720 a8083063 Iustin Pop
  """
721 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
722 a8083063 Iustin Pop
    return None
723 a8083063 Iustin Pop
724 a8083063 Iustin Pop
  f = open(filename)
725 a8083063 Iustin Pop
726 716a32cb Guido Trotter
  fp = compat.sha1_hash()
727 a8083063 Iustin Pop
  while True:
728 a8083063 Iustin Pop
    data = f.read(4096)
729 a8083063 Iustin Pop
    if not data:
730 a8083063 Iustin Pop
      break
731 a8083063 Iustin Pop
732 a8083063 Iustin Pop
    fp.update(data)
733 a8083063 Iustin Pop
734 a8083063 Iustin Pop
  return fp.hexdigest()
735 a8083063 Iustin Pop
736 a8083063 Iustin Pop
737 a8083063 Iustin Pop
def FingerprintFiles(files):
738 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
739 a8083063 Iustin Pop

740 58885d79 Iustin Pop
  @type files: list
741 58885d79 Iustin Pop
  @param files: the list of filename to fingerprint
742 58885d79 Iustin Pop
  @rtype: dict
743 58885d79 Iustin Pop
  @return: a dictionary filename: fingerprint, holding only
744 58885d79 Iustin Pop
      existing files
745 a8083063 Iustin Pop

746 a8083063 Iustin Pop
  """
747 a8083063 Iustin Pop
  ret = {}
748 a8083063 Iustin Pop
749 a8083063 Iustin Pop
  for filename in files:
750 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
751 a8083063 Iustin Pop
    if cksum:
752 a8083063 Iustin Pop
      ret[filename] = cksum
753 a8083063 Iustin Pop
754 a8083063 Iustin Pop
  return ret
755 a8083063 Iustin Pop
756 a8083063 Iustin Pop
757 a5728081 Guido Trotter
def ForceDictType(target, key_types, allowed_values=None):
758 a5728081 Guido Trotter
  """Force the values of a dict to have certain types.
759 a5728081 Guido Trotter

760 a5728081 Guido Trotter
  @type target: dict
761 a5728081 Guido Trotter
  @param target: the dict to update
762 a5728081 Guido Trotter
  @type key_types: dict
763 a5728081 Guido Trotter
  @param key_types: dict mapping target dict keys to types
764 a5728081 Guido Trotter
                    in constants.ENFORCEABLE_TYPES
765 a5728081 Guido Trotter
  @type allowed_values: list
766 a5728081 Guido Trotter
  @keyword allowed_values: list of specially allowed values
767 a5728081 Guido Trotter

768 a5728081 Guido Trotter
  """
769 a5728081 Guido Trotter
  if allowed_values is None:
770 a5728081 Guido Trotter
    allowed_values = []
771 a5728081 Guido Trotter
772 8b46606c Guido Trotter
  if not isinstance(target, dict):
773 8b46606c Guido Trotter
    msg = "Expected dictionary, got '%s'" % target
774 8b46606c Guido Trotter
    raise errors.TypeEnforcementError(msg)
775 8b46606c Guido Trotter
776 a5728081 Guido Trotter
  for key in target:
777 a5728081 Guido Trotter
    if key not in key_types:
778 a5728081 Guido Trotter
      msg = "Unknown key '%s'" % key
779 a5728081 Guido Trotter
      raise errors.TypeEnforcementError(msg)
780 a5728081 Guido Trotter
781 a5728081 Guido Trotter
    if target[key] in allowed_values:
782 a5728081 Guido Trotter
      continue
783 a5728081 Guido Trotter
784 29921401 Iustin Pop
    ktype = key_types[key]
785 29921401 Iustin Pop
    if ktype not in constants.ENFORCEABLE_TYPES:
786 29921401 Iustin Pop
      msg = "'%s' has non-enforceable type %s" % (key, ktype)
787 a5728081 Guido Trotter
      raise errors.ProgrammerError(msg)
788 a5728081 Guido Trotter
789 29921401 Iustin Pop
    if ktype == constants.VTYPE_STRING:
790 a5728081 Guido Trotter
      if not isinstance(target[key], basestring):
791 a5728081 Guido Trotter
        if isinstance(target[key], bool) and not target[key]:
792 a5728081 Guido Trotter
          target[key] = ''
793 a5728081 Guido Trotter
        else:
794 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid string" % (key, target[key])
795 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
796 29921401 Iustin Pop
    elif ktype == constants.VTYPE_BOOL:
797 a5728081 Guido Trotter
      if isinstance(target[key], basestring) and target[key]:
798 a5728081 Guido Trotter
        if target[key].lower() == constants.VALUE_FALSE:
799 a5728081 Guido Trotter
          target[key] = False
800 a5728081 Guido Trotter
        elif target[key].lower() == constants.VALUE_TRUE:
801 a5728081 Guido Trotter
          target[key] = True
802 a5728081 Guido Trotter
        else:
803 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid boolean" % (key, target[key])
804 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
805 a5728081 Guido Trotter
      elif target[key]:
806 a5728081 Guido Trotter
        target[key] = True
807 a5728081 Guido Trotter
      else:
808 a5728081 Guido Trotter
        target[key] = False
809 29921401 Iustin Pop
    elif ktype == constants.VTYPE_SIZE:
810 a5728081 Guido Trotter
      try:
811 a5728081 Guido Trotter
        target[key] = ParseUnit(target[key])
812 a5728081 Guido Trotter
      except errors.UnitParseError, err:
813 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid size. error: %s" % \
814 a5728081 Guido Trotter
              (key, target[key], err)
815 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
816 29921401 Iustin Pop
    elif ktype == constants.VTYPE_INT:
817 a5728081 Guido Trotter
      try:
818 a5728081 Guido Trotter
        target[key] = int(target[key])
819 a5728081 Guido Trotter
      except (ValueError, TypeError):
820 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid integer" % (key, target[key])
821 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
822 a5728081 Guido Trotter
823 a5728081 Guido Trotter
824 a01b500b Michael Hanselmann
def _GetProcStatusPath(pid):
825 a01b500b Michael Hanselmann
  """Returns the path for a PID's proc status file.
826 a01b500b Michael Hanselmann

827 a01b500b Michael Hanselmann
  @type pid: int
828 a01b500b Michael Hanselmann
  @param pid: Process ID
829 a01b500b Michael Hanselmann
  @rtype: string
830 a01b500b Michael Hanselmann

831 a01b500b Michael Hanselmann
  """
832 a01b500b Michael Hanselmann
  return "/proc/%d/status" % pid
833 a01b500b Michael Hanselmann
834 a01b500b Michael Hanselmann
835 a8083063 Iustin Pop
def IsProcessAlive(pid):
836 a8083063 Iustin Pop
  """Check if a given pid exists on the system.
837 a8083063 Iustin Pop

838 44bf25ff Iustin Pop
  @note: zombie status is not handled, so zombie processes
839 44bf25ff Iustin Pop
      will be returned as alive
840 58885d79 Iustin Pop
  @type pid: int
841 58885d79 Iustin Pop
  @param pid: the process ID to check
842 58885d79 Iustin Pop
  @rtype: boolean
843 58885d79 Iustin Pop
  @return: True if the process exists
844 a8083063 Iustin Pop

845 a8083063 Iustin Pop
  """
846 5ef5ea45 Guido Trotter
  def _TryStat(name):
847 5ef5ea45 Guido Trotter
    try:
848 5ef5ea45 Guido Trotter
      os.stat(name)
849 5ef5ea45 Guido Trotter
      return True
850 5ef5ea45 Guido Trotter
    except EnvironmentError, err:
851 5ef5ea45 Guido Trotter
      if err.errno in (errno.ENOENT, errno.ENOTDIR):
852 5ef5ea45 Guido Trotter
        return False
853 5ef5ea45 Guido Trotter
      elif err.errno == errno.EINVAL:
854 5ef5ea45 Guido Trotter
        raise RetryAgain(err)
855 5ef5ea45 Guido Trotter
      raise
856 5ef5ea45 Guido Trotter
857 5ef5ea45 Guido Trotter
  assert isinstance(pid, int), "pid must be an integer"
858 d9f311d7 Iustin Pop
  if pid <= 0:
859 d9f311d7 Iustin Pop
    return False
860 d9f311d7 Iustin Pop
861 5ef5ea45 Guido Trotter
  # /proc in a multiprocessor environment can have strange behaviors.
862 5ef5ea45 Guido Trotter
  # Retry the os.stat a few times until we get a good result.
863 a8083063 Iustin Pop
  try:
864 a01b500b Michael Hanselmann
    return Retry(_TryStat, (0.01, 1.5, 0.1), 0.5,
865 a01b500b Michael Hanselmann
                 args=[_GetProcStatusPath(pid)])
866 5ef5ea45 Guido Trotter
  except RetryTimeout, err:
867 5ef5ea45 Guido Trotter
    err.RaiseInner()
868 a8083063 Iustin Pop
869 a8083063 Iustin Pop
870 a01b500b Michael Hanselmann
def _ParseSigsetT(sigset):
871 a01b500b Michael Hanselmann
  """Parse a rendered sigset_t value.
872 a01b500b Michael Hanselmann

873 a01b500b Michael Hanselmann
  This is the opposite of the Linux kernel's fs/proc/array.c:render_sigset_t
874 a01b500b Michael Hanselmann
  function.
875 a01b500b Michael Hanselmann

876 a01b500b Michael Hanselmann
  @type sigset: string
877 a01b500b Michael Hanselmann
  @param sigset: Rendered signal set from /proc/$pid/status
878 a01b500b Michael Hanselmann
  @rtype: set
879 a01b500b Michael Hanselmann
  @return: Set of all enabled signal numbers
880 a01b500b Michael Hanselmann

881 a01b500b Michael Hanselmann
  """
882 a01b500b Michael Hanselmann
  result = set()
883 a01b500b Michael Hanselmann
884 a01b500b Michael Hanselmann
  signum = 0
885 a01b500b Michael Hanselmann
  for ch in reversed(sigset):
886 a01b500b Michael Hanselmann
    chv = int(ch, 16)
887 a01b500b Michael Hanselmann
888 a01b500b Michael Hanselmann
    # The following could be done in a loop, but it's easier to read and
889 a01b500b Michael Hanselmann
    # understand in the unrolled form
890 a01b500b Michael Hanselmann
    if chv & 1:
891 a01b500b Michael Hanselmann
      result.add(signum + 1)
892 a01b500b Michael Hanselmann
    if chv & 2:
893 a01b500b Michael Hanselmann
      result.add(signum + 2)
894 a01b500b Michael Hanselmann
    if chv & 4:
895 a01b500b Michael Hanselmann
      result.add(signum + 3)
896 a01b500b Michael Hanselmann
    if chv & 8:
897 a01b500b Michael Hanselmann
      result.add(signum + 4)
898 a01b500b Michael Hanselmann
899 a01b500b Michael Hanselmann
    signum += 4
900 a01b500b Michael Hanselmann
901 a01b500b Michael Hanselmann
  return result
902 a01b500b Michael Hanselmann
903 a01b500b Michael Hanselmann
904 a01b500b Michael Hanselmann
def _GetProcStatusField(pstatus, field):
905 a01b500b Michael Hanselmann
  """Retrieves a field from the contents of a proc status file.
906 a01b500b Michael Hanselmann

907 a01b500b Michael Hanselmann
  @type pstatus: string
908 a01b500b Michael Hanselmann
  @param pstatus: Contents of /proc/$pid/status
909 a01b500b Michael Hanselmann
  @type field: string
910 a01b500b Michael Hanselmann
  @param field: Name of field whose value should be returned
911 a01b500b Michael Hanselmann
  @rtype: string
912 a01b500b Michael Hanselmann

913 a01b500b Michael Hanselmann
  """
914 a01b500b Michael Hanselmann
  for line in pstatus.splitlines():
915 a01b500b Michael Hanselmann
    parts = line.split(":", 1)
916 a01b500b Michael Hanselmann
917 a01b500b Michael Hanselmann
    if len(parts) < 2 or parts[0] != field:
918 a01b500b Michael Hanselmann
      continue
919 a01b500b Michael Hanselmann
920 a01b500b Michael Hanselmann
    return parts[1].strip()
921 a01b500b Michael Hanselmann
922 a01b500b Michael Hanselmann
  return None
923 a01b500b Michael Hanselmann
924 a01b500b Michael Hanselmann
925 a01b500b Michael Hanselmann
def IsProcessHandlingSignal(pid, signum, status_path=None):
926 a01b500b Michael Hanselmann
  """Checks whether a process is handling a signal.
927 a01b500b Michael Hanselmann

928 a01b500b Michael Hanselmann
  @type pid: int
929 a01b500b Michael Hanselmann
  @param pid: Process ID
930 a01b500b Michael Hanselmann
  @type signum: int
931 a01b500b Michael Hanselmann
  @param signum: Signal number
932 a01b500b Michael Hanselmann
  @rtype: bool
933 a01b500b Michael Hanselmann

934 a01b500b Michael Hanselmann
  """
935 a01b500b Michael Hanselmann
  if status_path is None:
936 a01b500b Michael Hanselmann
    status_path = _GetProcStatusPath(pid)
937 a01b500b Michael Hanselmann
938 a01b500b Michael Hanselmann
  try:
939 a01b500b Michael Hanselmann
    proc_status = ReadFile(status_path)
940 a01b500b Michael Hanselmann
  except EnvironmentError, err:
941 a01b500b Michael Hanselmann
    # In at least one case, reading /proc/$pid/status failed with ESRCH.
942 a01b500b Michael Hanselmann
    if err.errno in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL, errno.ESRCH):
943 a01b500b Michael Hanselmann
      return False
944 a01b500b Michael Hanselmann
    raise
945 a01b500b Michael Hanselmann
946 a01b500b Michael Hanselmann
  sigcgt = _GetProcStatusField(proc_status, "SigCgt")
947 a01b500b Michael Hanselmann
  if sigcgt is None:
948 a01b500b Michael Hanselmann
    raise RuntimeError("%s is missing 'SigCgt' field" % status_path)
949 a01b500b Michael Hanselmann
950 a01b500b Michael Hanselmann
  # Now check whether signal is handled
951 a01b500b Michael Hanselmann
  return signum in _ParseSigsetT(sigcgt)
952 a01b500b Michael Hanselmann
953 a01b500b Michael Hanselmann
954 d9f311d7 Iustin Pop
def ReadPidFile(pidfile):
955 58885d79 Iustin Pop
  """Read a pid from a file.
956 fee80e90 Guido Trotter

957 58885d79 Iustin Pop
  @type  pidfile: string
958 58885d79 Iustin Pop
  @param pidfile: path to the file containing the pid
959 58885d79 Iustin Pop
  @rtype: int
960 1de62f37 Guido Trotter
  @return: The process id, if the file exists and contains a valid PID,
961 d9f311d7 Iustin Pop
           otherwise 0
962 fee80e90 Guido Trotter

963 fee80e90 Guido Trotter
  """
964 fee80e90 Guido Trotter
  try:
965 682f7601 Guido Trotter
    raw_data = ReadOneLineFile(pidfile)
966 d9f311d7 Iustin Pop
  except EnvironmentError, err:
967 d9f311d7 Iustin Pop
    if err.errno != errno.ENOENT:
968 13998ef2 Michael Hanselmann
      logging.exception("Can't read pid file")
969 d9f311d7 Iustin Pop
    return 0
970 fee80e90 Guido Trotter
971 fee80e90 Guido Trotter
  try:
972 13998ef2 Michael Hanselmann
    pid = int(raw_data)
973 691744c4 Iustin Pop
  except (TypeError, ValueError), err:
974 8161a646 Iustin Pop
    logging.info("Can't parse pid file contents", exc_info=True)
975 d9f311d7 Iustin Pop
    return 0
976 fee80e90 Guido Trotter
977 d9f311d7 Iustin Pop
  return pid
978 fee80e90 Guido Trotter
979 fee80e90 Guido Trotter
980 debed9ae Michael Hanselmann
def ReadLockedPidFile(path):
981 debed9ae Michael Hanselmann
  """Reads a locked PID file.
982 debed9ae Michael Hanselmann

983 debed9ae Michael Hanselmann
  This can be used together with L{StartDaemon}.
984 debed9ae Michael Hanselmann

985 debed9ae Michael Hanselmann
  @type path: string
986 debed9ae Michael Hanselmann
  @param path: Path to PID file
987 debed9ae Michael Hanselmann
  @return: PID as integer or, if file was unlocked or couldn't be opened, None
988 debed9ae Michael Hanselmann

989 debed9ae Michael Hanselmann
  """
990 debed9ae Michael Hanselmann
  try:
991 debed9ae Michael Hanselmann
    fd = os.open(path, os.O_RDONLY)
992 debed9ae Michael Hanselmann
  except EnvironmentError, err:
993 debed9ae Michael Hanselmann
    if err.errno == errno.ENOENT:
994 debed9ae Michael Hanselmann
      # PID file doesn't exist
995 debed9ae Michael Hanselmann
      return None
996 debed9ae Michael Hanselmann
    raise
997 debed9ae Michael Hanselmann
998 debed9ae Michael Hanselmann
  try:
999 debed9ae Michael Hanselmann
    try:
1000 debed9ae Michael Hanselmann
      # Try to acquire lock
1001 debed9ae Michael Hanselmann
      LockFile(fd)
1002 debed9ae Michael Hanselmann
    except errors.LockError:
1003 debed9ae Michael Hanselmann
      # Couldn't lock, daemon is running
1004 debed9ae Michael Hanselmann
      return int(os.read(fd, 100))
1005 debed9ae Michael Hanselmann
  finally:
1006 debed9ae Michael Hanselmann
    os.close(fd)
1007 debed9ae Michael Hanselmann
1008 debed9ae Michael Hanselmann
  return None
1009 debed9ae Michael Hanselmann
1010 debed9ae Michael Hanselmann
1011 256eb94b Guido Trotter
def MatchNameComponent(key, name_list, case_sensitive=True):
1012 a8083063 Iustin Pop
  """Try to match a name against a list.
1013 a8083063 Iustin Pop

1014 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
1015 58885d79 Iustin Pop
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
1016 58885d79 Iustin Pop
  this list, I{'test1'} as well as I{'test1.example'} will match, but
1017 58885d79 Iustin Pop
  not I{'test1.ex'}. A multiple match will be considered as no match
1018 58885d79 Iustin Pop
  at all (e.g. I{'test1'} against C{['test1.example.com',
1019 3a541d90 Iustin Pop
  'test1.example.org']}), except when the key fully matches an entry
1020 3a541d90 Iustin Pop
  (e.g. I{'test1'} against C{['test1', 'test1.example.com']}).
1021 a8083063 Iustin Pop

1022 58885d79 Iustin Pop
  @type key: str
1023 58885d79 Iustin Pop
  @param key: the name to be searched
1024 58885d79 Iustin Pop
  @type name_list: list
1025 58885d79 Iustin Pop
  @param name_list: the list of strings against which to search the key
1026 256eb94b Guido Trotter
  @type case_sensitive: boolean
1027 256eb94b Guido Trotter
  @param case_sensitive: whether to provide a case-sensitive match
1028 a8083063 Iustin Pop

1029 58885d79 Iustin Pop
  @rtype: None or str
1030 58885d79 Iustin Pop
  @return: None if there is no match I{or} if there are multiple matches,
1031 58885d79 Iustin Pop
      otherwise the element from the list which matches
1032 a8083063 Iustin Pop

1033 a8083063 Iustin Pop
  """
1034 3a541d90 Iustin Pop
  if key in name_list:
1035 3a541d90 Iustin Pop
    return key
1036 256eb94b Guido Trotter
1037 256eb94b Guido Trotter
  re_flags = 0
1038 256eb94b Guido Trotter
  if not case_sensitive:
1039 256eb94b Guido Trotter
    re_flags |= re.IGNORECASE
1040 099c52ad Iustin Pop
    key = key.upper()
1041 256eb94b Guido Trotter
  mo = re.compile("^%s(\..*)?$" % re.escape(key), re_flags)
1042 256eb94b Guido Trotter
  names_filtered = []
1043 256eb94b Guido Trotter
  string_matches = []
1044 256eb94b Guido Trotter
  for name in name_list:
1045 256eb94b Guido Trotter
    if mo.match(name) is not None:
1046 256eb94b Guido Trotter
      names_filtered.append(name)
1047 099c52ad Iustin Pop
      if not case_sensitive and key == name.upper():
1048 256eb94b Guido Trotter
        string_matches.append(name)
1049 256eb94b Guido Trotter
1050 256eb94b Guido Trotter
  if len(string_matches) == 1:
1051 256eb94b Guido Trotter
    return string_matches[0]
1052 256eb94b Guido Trotter
  if len(names_filtered) == 1:
1053 256eb94b Guido Trotter
    return names_filtered[0]
1054 256eb94b Guido Trotter
  return None
1055 a8083063 Iustin Pop
1056 a8083063 Iustin Pop
1057 28f34048 Michael Hanselmann
def ValidateServiceName(name):
1058 28f34048 Michael Hanselmann
  """Validate the given service name.
1059 28f34048 Michael Hanselmann

1060 28f34048 Michael Hanselmann
  @type name: number or string
1061 28f34048 Michael Hanselmann
  @param name: Service name or port specification
1062 28f34048 Michael Hanselmann

1063 28f34048 Michael Hanselmann
  """
1064 28f34048 Michael Hanselmann
  try:
1065 28f34048 Michael Hanselmann
    numport = int(name)
1066 28f34048 Michael Hanselmann
  except (ValueError, TypeError):
1067 28f34048 Michael Hanselmann
    # Non-numeric service name
1068 28f34048 Michael Hanselmann
    valid = _VALID_SERVICE_NAME_RE.match(name)
1069 28f34048 Michael Hanselmann
  else:
1070 28f34048 Michael Hanselmann
    # Numeric port (protocols other than TCP or UDP might need adjustments
1071 28f34048 Michael Hanselmann
    # here)
1072 28f34048 Michael Hanselmann
    valid = (numport >= 0 and numport < (1 << 16))
1073 28f34048 Michael Hanselmann
1074 28f34048 Michael Hanselmann
  if not valid:
1075 28f34048 Michael Hanselmann
    raise errors.OpPrereqError("Invalid service name '%s'" % name,
1076 28f34048 Michael Hanselmann
                               errors.ECODE_INVAL)
1077 28f34048 Michael Hanselmann
1078 28f34048 Michael Hanselmann
  return name
1079 28f34048 Michael Hanselmann
1080 28f34048 Michael Hanselmann
1081 a8083063 Iustin Pop
def ListVolumeGroups():
1082 a8083063 Iustin Pop
  """List volume groups and their size
1083 a8083063 Iustin Pop

1084 58885d79 Iustin Pop
  @rtype: dict
1085 58885d79 Iustin Pop
  @return:
1086 58885d79 Iustin Pop
       Dictionary with keys volume name and values
1087 58885d79 Iustin Pop
       the size of the volume
1088 a8083063 Iustin Pop

1089 a8083063 Iustin Pop
  """
1090 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
1091 a8083063 Iustin Pop
  result = RunCmd(command)
1092 a8083063 Iustin Pop
  retval = {}
1093 a8083063 Iustin Pop
  if result.failed:
1094 a8083063 Iustin Pop
    return retval
1095 a8083063 Iustin Pop
1096 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
1097 a8083063 Iustin Pop
    try:
1098 a8083063 Iustin Pop
      name, size = line.split()
1099 a8083063 Iustin Pop
      size = int(float(size))
1100 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
1101 bb698c1f Iustin Pop
      logging.error("Invalid output from vgs (%s): %s", err, line)
1102 a8083063 Iustin Pop
      continue
1103 a8083063 Iustin Pop
1104 a8083063 Iustin Pop
    retval[name] = size
1105 a8083063 Iustin Pop
1106 a8083063 Iustin Pop
  return retval
1107 a8083063 Iustin Pop
1108 a8083063 Iustin Pop
1109 a8083063 Iustin Pop
def BridgeExists(bridge):
1110 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
1111 a8083063 Iustin Pop

1112 58885d79 Iustin Pop
  @type bridge: str
1113 58885d79 Iustin Pop
  @param bridge: the bridge name to check
1114 58885d79 Iustin Pop
  @rtype: boolean
1115 58885d79 Iustin Pop
  @return: True if it does
1116 a8083063 Iustin Pop

1117 a8083063 Iustin Pop
  """
1118 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
1119 a8083063 Iustin Pop
1120 a8083063 Iustin Pop
1121 a8083063 Iustin Pop
def NiceSort(name_list):
1122 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
1123 a8083063 Iustin Pop

1124 58885d79 Iustin Pop
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
1125 58885d79 Iustin Pop
  will sort the list in the logical order C{['a1', 'a2', 'a10',
1126 58885d79 Iustin Pop
  'a11']}.
1127 a8083063 Iustin Pop

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

1132 58885d79 Iustin Pop
  @type name_list: list
1133 58885d79 Iustin Pop
  @param name_list: the names to be sorted
1134 58885d79 Iustin Pop
  @rtype: list
1135 58885d79 Iustin Pop
  @return: a copy of the name list sorted with our algorithm
1136 a8083063 Iustin Pop

1137 a8083063 Iustin Pop
  """
1138 a8083063 Iustin Pop
  _SORTER_BASE = "(\D+|\d+)"
1139 a8083063 Iustin Pop
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
1140 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
1141 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
1142 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE)
1143 a8083063 Iustin Pop
  _SORTER_RE = re.compile(_SORTER_FULL)
1144 a8083063 Iustin Pop
  _SORTER_NODIGIT = re.compile("^\D*$")
1145 a8083063 Iustin Pop
  def _TryInt(val):
1146 a8083063 Iustin Pop
    """Attempts to convert a variable to integer."""
1147 a8083063 Iustin Pop
    if val is None or _SORTER_NODIGIT.match(val):
1148 a8083063 Iustin Pop
      return val
1149 a8083063 Iustin Pop
    rval = int(val)
1150 a8083063 Iustin Pop
    return rval
1151 a8083063 Iustin Pop
1152 a8083063 Iustin Pop
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
1153 a8083063 Iustin Pop
             for name in name_list]
1154 a8083063 Iustin Pop
  to_sort.sort()
1155 a8083063 Iustin Pop
  return [tup[1] for tup in to_sort]
1156 a8083063 Iustin Pop
1157 a8083063 Iustin Pop
1158 a8083063 Iustin Pop
def TryConvert(fn, val):
1159 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
1160 a8083063 Iustin Pop

1161 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
1162 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
1163 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
1164 58885d79 Iustin Pop
  exceptions are propagated to the caller.
1165 58885d79 Iustin Pop

1166 58885d79 Iustin Pop
  @type fn: callable
1167 58885d79 Iustin Pop
  @param fn: function to apply to the value
1168 58885d79 Iustin Pop
  @param val: the value to be converted
1169 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
1170 58885d79 Iustin Pop
      otherwise the original value.
1171 a8083063 Iustin Pop

1172 a8083063 Iustin Pop
  """
1173 a8083063 Iustin Pop
  try:
1174 a8083063 Iustin Pop
    nv = fn(val)
1175 7c4d6c7b Michael Hanselmann
  except (ValueError, TypeError):
1176 a8083063 Iustin Pop
    nv = val
1177 a8083063 Iustin Pop
  return nv
1178 a8083063 Iustin Pop
1179 a8083063 Iustin Pop
1180 a8083063 Iustin Pop
def IsValidShellParam(word):
1181 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
1182 a8083063 Iustin Pop

1183 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
1184 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
1185 a8083063 Iustin Pop
  the actual command.
1186 a8083063 Iustin Pop

1187 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
1188 a8083063 Iustin Pop
  side.
1189 a8083063 Iustin Pop

1190 58885d79 Iustin Pop
  @type word: str
1191 58885d79 Iustin Pop
  @param word: the word to check
1192 58885d79 Iustin Pop
  @rtype: boolean
1193 58885d79 Iustin Pop
  @return: True if the word is 'safe'
1194 58885d79 Iustin Pop

1195 a8083063 Iustin Pop
  """
1196 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
1197 a8083063 Iustin Pop
1198 a8083063 Iustin Pop
1199 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
1200 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
1201 a8083063 Iustin Pop

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

1207 58885d79 Iustin Pop
  @type template: str
1208 58885d79 Iustin Pop
  @param template: the string holding the template for the
1209 58885d79 Iustin Pop
      string formatting
1210 58885d79 Iustin Pop
  @rtype: str
1211 58885d79 Iustin Pop
  @return: the expanded command line
1212 58885d79 Iustin Pop

1213 a8083063 Iustin Pop
  """
1214 a8083063 Iustin Pop
  for word in args:
1215 a8083063 Iustin Pop
    if not IsValidShellParam(word):
1216 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
1217 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
1218 a8083063 Iustin Pop
  return template % args
1219 a8083063 Iustin Pop
1220 a8083063 Iustin Pop
1221 9fbfbb7b Iustin Pop
def FormatUnit(value, units):
1222 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
1223 a8083063 Iustin Pop

1224 58885d79 Iustin Pop
  @type value: int
1225 58885d79 Iustin Pop
  @param value: integer representing the value in MiB (1048576)
1226 9fbfbb7b Iustin Pop
  @type units: char
1227 9fbfbb7b Iustin Pop
  @param units: the type of formatting we should do:
1228 9fbfbb7b Iustin Pop
      - 'h' for automatic scaling
1229 9fbfbb7b Iustin Pop
      - 'm' for MiBs
1230 9fbfbb7b Iustin Pop
      - 'g' for GiBs
1231 9fbfbb7b Iustin Pop
      - 't' for TiBs
1232 58885d79 Iustin Pop
  @rtype: str
1233 58885d79 Iustin Pop
  @return: the formatted value (with suffix)
1234 a8083063 Iustin Pop

1235 a8083063 Iustin Pop
  """
1236 9fbfbb7b Iustin Pop
  if units not in ('m', 'g', 't', 'h'):
1237 9fbfbb7b Iustin Pop
    raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
1238 a8083063 Iustin Pop
1239 9fbfbb7b Iustin Pop
  suffix = ''
1240 9fbfbb7b Iustin Pop
1241 9fbfbb7b Iustin Pop
  if units == 'm' or (units == 'h' and value < 1024):
1242 9fbfbb7b Iustin Pop
    if units == 'h':
1243 9fbfbb7b Iustin Pop
      suffix = 'M'
1244 9fbfbb7b Iustin Pop
    return "%d%s" % (round(value, 0), suffix)
1245 9fbfbb7b Iustin Pop
1246 9fbfbb7b Iustin Pop
  elif units == 'g' or (units == 'h' and value < (1024 * 1024)):
1247 9fbfbb7b Iustin Pop
    if units == 'h':
1248 9fbfbb7b Iustin Pop
      suffix = 'G'
1249 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024, 1), suffix)
1250 a8083063 Iustin Pop
1251 a8083063 Iustin Pop
  else:
1252 9fbfbb7b Iustin Pop
    if units == 'h':
1253 9fbfbb7b Iustin Pop
      suffix = 'T'
1254 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
1255 a8083063 Iustin Pop
1256 a8083063 Iustin Pop
1257 a8083063 Iustin Pop
def ParseUnit(input_string):
1258 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
1259 a8083063 Iustin Pop

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

1264 a8083063 Iustin Pop
  """
1265 9939547b Iustin Pop
  m = re.match('^([.\d]+)\s*([a-zA-Z]+)?$', str(input_string))
1266 a8083063 Iustin Pop
  if not m:
1267 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Invalid format")
1268 a8083063 Iustin Pop
1269 a8083063 Iustin Pop
  value = float(m.groups()[0])
1270 a8083063 Iustin Pop
1271 a8083063 Iustin Pop
  unit = m.groups()[1]
1272 a8083063 Iustin Pop
  if unit:
1273 a8083063 Iustin Pop
    lcunit = unit.lower()
1274 a8083063 Iustin Pop
  else:
1275 a8083063 Iustin Pop
    lcunit = 'm'
1276 a8083063 Iustin Pop
1277 a8083063 Iustin Pop
  if lcunit in ('m', 'mb', 'mib'):
1278 a8083063 Iustin Pop
    # Value already in MiB
1279 a8083063 Iustin Pop
    pass
1280 a8083063 Iustin Pop
1281 a8083063 Iustin Pop
  elif lcunit in ('g', 'gb', 'gib'):
1282 a8083063 Iustin Pop
    value *= 1024
1283 a8083063 Iustin Pop
1284 a8083063 Iustin Pop
  elif lcunit in ('t', 'tb', 'tib'):
1285 a8083063 Iustin Pop
    value *= 1024 * 1024
1286 a8083063 Iustin Pop
1287 a8083063 Iustin Pop
  else:
1288 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Unknown unit: %s" % unit)
1289 a8083063 Iustin Pop
1290 a8083063 Iustin Pop
  # Make sure we round up
1291 a8083063 Iustin Pop
  if int(value) < value:
1292 a8083063 Iustin Pop
    value += 1
1293 a8083063 Iustin Pop
1294 a8083063 Iustin Pop
  # Round up to the next multiple of 4
1295 a8083063 Iustin Pop
  value = int(value)
1296 a8083063 Iustin Pop
  if value % 4:
1297 a8083063 Iustin Pop
    value += 4 - value % 4
1298 a8083063 Iustin Pop
1299 a8083063 Iustin Pop
  return value
1300 a8083063 Iustin Pop
1301 a8083063 Iustin Pop
1302 a8083063 Iustin Pop
def AddAuthorizedKey(file_name, key):
1303 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
1304 a8083063 Iustin Pop

1305 58885d79 Iustin Pop
  @type file_name: str
1306 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
1307 58885d79 Iustin Pop
  @type key: str
1308 58885d79 Iustin Pop
  @param key: string containing key
1309 58885d79 Iustin Pop

1310 a8083063 Iustin Pop
  """
1311 a8083063 Iustin Pop
  key_fields = key.split()
1312 a8083063 Iustin Pop
1313 a8083063 Iustin Pop
  f = open(file_name, 'a+')
1314 a8083063 Iustin Pop
  try:
1315 a8083063 Iustin Pop
    nl = True
1316 a8083063 Iustin Pop
    for line in f:
1317 a8083063 Iustin Pop
      # Ignore whitespace changes
1318 a8083063 Iustin Pop
      if line.split() == key_fields:
1319 a8083063 Iustin Pop
        break
1320 a8083063 Iustin Pop
      nl = line.endswith('\n')
1321 a8083063 Iustin Pop
    else:
1322 a8083063 Iustin Pop
      if not nl:
1323 a8083063 Iustin Pop
        f.write("\n")
1324 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
1325 a8083063 Iustin Pop
      f.write("\n")
1326 a8083063 Iustin Pop
      f.flush()
1327 a8083063 Iustin Pop
  finally:
1328 a8083063 Iustin Pop
    f.close()
1329 a8083063 Iustin Pop
1330 a8083063 Iustin Pop
1331 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
1332 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
1333 a8083063 Iustin Pop

1334 58885d79 Iustin Pop
  @type file_name: str
1335 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
1336 58885d79 Iustin Pop
  @type key: str
1337 58885d79 Iustin Pop
  @param key: string containing key
1338 58885d79 Iustin Pop

1339 a8083063 Iustin Pop
  """
1340 a8083063 Iustin Pop
  key_fields = key.split()
1341 a8083063 Iustin Pop
1342 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1343 a8083063 Iustin Pop
  try:
1344 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
1345 a8083063 Iustin Pop
    try:
1346 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
1347 59f82e3f Michael Hanselmann
      try:
1348 59f82e3f Michael Hanselmann
        for line in f:
1349 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
1350 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
1351 59f82e3f Michael Hanselmann
            out.write(line)
1352 899d2a81 Michael Hanselmann
1353 899d2a81 Michael Hanselmann
        out.flush()
1354 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
1355 899d2a81 Michael Hanselmann
      finally:
1356 899d2a81 Michael Hanselmann
        f.close()
1357 899d2a81 Michael Hanselmann
    finally:
1358 899d2a81 Michael Hanselmann
      out.close()
1359 899d2a81 Michael Hanselmann
  except:
1360 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
1361 899d2a81 Michael Hanselmann
    raise
1362 899d2a81 Michael Hanselmann
1363 899d2a81 Michael Hanselmann
1364 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
1365 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
1366 899d2a81 Michael Hanselmann

1367 58885d79 Iustin Pop
  @type file_name: str
1368 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1369 58885d79 Iustin Pop
  @type ip: str
1370 58885d79 Iustin Pop
  @param ip: the IP address
1371 58885d79 Iustin Pop
  @type hostname: str
1372 58885d79 Iustin Pop
  @param hostname: the hostname to be added
1373 58885d79 Iustin Pop
  @type aliases: list
1374 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
1375 58885d79 Iustin Pop

1376 899d2a81 Michael Hanselmann
  """
1377 9b977740 Guido Trotter
  # FIXME: use WriteFile + fn rather than duplicating its efforts
1378 7fbb1f65 Michael Hanselmann
  # Ensure aliases are unique
1379 7fbb1f65 Michael Hanselmann
  aliases = UniqueSequence([hostname] + aliases)[1:]
1380 7fbb1f65 Michael Hanselmann
1381 9440aeab Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1382 899d2a81 Michael Hanselmann
  try:
1383 9440aeab Michael Hanselmann
    out = os.fdopen(fd, 'w')
1384 9440aeab Michael Hanselmann
    try:
1385 9440aeab Michael Hanselmann
      f = open(file_name, 'r')
1386 9440aeab Michael Hanselmann
      try:
1387 9440aeab Michael Hanselmann
        for line in f:
1388 9440aeab Michael Hanselmann
          fields = line.split()
1389 7e3dbb94 Iustin Pop
          if fields and not fields[0].startswith('#') and ip == fields[0]:
1390 9440aeab Michael Hanselmann
            continue
1391 9440aeab Michael Hanselmann
          out.write(line)
1392 9440aeab Michael Hanselmann
1393 7e3dbb94 Iustin Pop
        out.write("%s\t%s" % (ip, hostname))
1394 9440aeab Michael Hanselmann
        if aliases:
1395 9440aeab Michael Hanselmann
          out.write(" %s" % ' '.join(aliases))
1396 9440aeab Michael Hanselmann
        out.write('\n')
1397 9440aeab Michael Hanselmann
1398 9440aeab Michael Hanselmann
        out.flush()
1399 2e3e75b7 Michael Hanselmann
        os.fsync(out)
1400 9b977740 Guido Trotter
        os.chmod(tmpname, 0644)
1401 9440aeab Michael Hanselmann
        os.rename(tmpname, file_name)
1402 9440aeab Michael Hanselmann
      finally:
1403 9440aeab Michael Hanselmann
        f.close()
1404 9440aeab Michael Hanselmann
    finally:
1405 9440aeab Michael Hanselmann
      out.close()
1406 9440aeab Michael Hanselmann
  except:
1407 9440aeab Michael Hanselmann
    RemoveFile(tmpname)
1408 9440aeab Michael Hanselmann
    raise
1409 899d2a81 Michael Hanselmann
1410 899d2a81 Michael Hanselmann
1411 d9c02ca6 Michael Hanselmann
def AddHostToEtcHosts(hostname):
1412 d9c02ca6 Michael Hanselmann
  """Wrapper around SetEtcHostsEntry.
1413 d9c02ca6 Michael Hanselmann

1414 58885d79 Iustin Pop
  @type hostname: str
1415 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
1416 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1417 58885d79 Iustin Pop

1418 d9c02ca6 Michael Hanselmann
  """
1419 a744b676 Manuel Franceschini
  hi = netutils.HostInfo(name=hostname)
1420 d9c02ca6 Michael Hanselmann
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
1421 d9c02ca6 Michael Hanselmann
1422 d9c02ca6 Michael Hanselmann
1423 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
1424 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
1425 899d2a81 Michael Hanselmann

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

1428 58885d79 Iustin Pop
  @type file_name: str
1429 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1430 58885d79 Iustin Pop
  @type hostname: str
1431 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
1432 58885d79 Iustin Pop

1433 899d2a81 Michael Hanselmann
  """
1434 9b977740 Guido Trotter
  # FIXME: use WriteFile + fn rather than duplicating its efforts
1435 899d2a81 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1436 899d2a81 Michael Hanselmann
  try:
1437 899d2a81 Michael Hanselmann
    out = os.fdopen(fd, 'w')
1438 899d2a81 Michael Hanselmann
    try:
1439 899d2a81 Michael Hanselmann
      f = open(file_name, 'r')
1440 899d2a81 Michael Hanselmann
      try:
1441 899d2a81 Michael Hanselmann
        for line in f:
1442 899d2a81 Michael Hanselmann
          fields = line.split()
1443 899d2a81 Michael Hanselmann
          if len(fields) > 1 and not fields[0].startswith('#'):
1444 899d2a81 Michael Hanselmann
            names = fields[1:]
1445 899d2a81 Michael Hanselmann
            if hostname in names:
1446 899d2a81 Michael Hanselmann
              while hostname in names:
1447 899d2a81 Michael Hanselmann
                names.remove(hostname)
1448 899d2a81 Michael Hanselmann
              if names:
1449 9440aeab Michael Hanselmann
                out.write("%s %s\n" % (fields[0], ' '.join(names)))
1450 899d2a81 Michael Hanselmann
              continue
1451 899d2a81 Michael Hanselmann
1452 899d2a81 Michael Hanselmann
          out.write(line)
1453 59f82e3f Michael Hanselmann
1454 59f82e3f Michael Hanselmann
        out.flush()
1455 2e3e75b7 Michael Hanselmann
        os.fsync(out)
1456 9b977740 Guido Trotter
        os.chmod(tmpname, 0644)
1457 59f82e3f Michael Hanselmann
        os.rename(tmpname, file_name)
1458 59f82e3f Michael Hanselmann
      finally:
1459 59f82e3f Michael Hanselmann
        f.close()
1460 a8083063 Iustin Pop
    finally:
1461 59f82e3f Michael Hanselmann
      out.close()
1462 59f82e3f Michael Hanselmann
  except:
1463 59f82e3f Michael Hanselmann
    RemoveFile(tmpname)
1464 59f82e3f Michael Hanselmann
    raise
1465 a8083063 Iustin Pop
1466 a8083063 Iustin Pop
1467 d9c02ca6 Michael Hanselmann
def RemoveHostFromEtcHosts(hostname):
1468 d9c02ca6 Michael Hanselmann
  """Wrapper around RemoveEtcHostsEntry.
1469 d9c02ca6 Michael Hanselmann

1470 58885d79 Iustin Pop
  @type hostname: str
1471 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
1472 58885d79 Iustin Pop
      full and shot name will be removed from
1473 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1474 58885d79 Iustin Pop

1475 d9c02ca6 Michael Hanselmann
  """
1476 a744b676 Manuel Franceschini
  hi = netutils.HostInfo(name=hostname)
1477 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
1478 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
1479 d9c02ca6 Michael Hanselmann
1480 d9c02ca6 Michael Hanselmann
1481 1d466a4f Michael Hanselmann
def TimestampForFilename():
1482 1d466a4f Michael Hanselmann
  """Returns the current time formatted for filenames.
1483 1d466a4f Michael Hanselmann

1484 1d466a4f Michael Hanselmann
  The format doesn't contain colons as some shells and applications them as
1485 1d466a4f Michael Hanselmann
  separators.
1486 1d466a4f Michael Hanselmann

1487 1d466a4f Michael Hanselmann
  """
1488 1d466a4f Michael Hanselmann
  return time.strftime("%Y-%m-%d_%H_%M_%S")
1489 1d466a4f Michael Hanselmann
1490 1d466a4f Michael Hanselmann
1491 a8083063 Iustin Pop
def CreateBackup(file_name):
1492 a8083063 Iustin Pop
  """Creates a backup of a file.
1493 a8083063 Iustin Pop

1494 58885d79 Iustin Pop
  @type file_name: str
1495 58885d79 Iustin Pop
  @param file_name: file to be backed up
1496 58885d79 Iustin Pop
  @rtype: str
1497 58885d79 Iustin Pop
  @return: the path to the newly created backup
1498 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
1499 a8083063 Iustin Pop

1500 a8083063 Iustin Pop
  """
1501 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
1502 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
1503 3ecf6786 Iustin Pop
                                file_name)
1504 a8083063 Iustin Pop
1505 1d466a4f Michael Hanselmann
  prefix = ("%s.backup-%s." %
1506 1d466a4f Michael Hanselmann
            (os.path.basename(file_name), TimestampForFilename()))
1507 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
1508 081b1e69 Michael Hanselmann
1509 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
1510 081b1e69 Michael Hanselmann
  try:
1511 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
1512 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
1513 081b1e69 Michael Hanselmann
    try:
1514 1d466a4f Michael Hanselmann
      logging.debug("Backing up %s at %s", file_name, backup_name)
1515 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
1516 081b1e69 Michael Hanselmann
    finally:
1517 081b1e69 Michael Hanselmann
      fdst.close()
1518 081b1e69 Michael Hanselmann
  finally:
1519 081b1e69 Michael Hanselmann
    fsrc.close()
1520 081b1e69 Michael Hanselmann
1521 a8083063 Iustin Pop
  return backup_name
1522 a8083063 Iustin Pop
1523 a8083063 Iustin Pop
1524 a8083063 Iustin Pop
def ShellQuote(value):
1525 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
1526 3ecf6786 Iustin Pop

1527 58885d79 Iustin Pop
  @type value: str
1528 58885d79 Iustin Pop
  @param value: the argument to be quoted
1529 58885d79 Iustin Pop
  @rtype: str
1530 58885d79 Iustin Pop
  @return: the quoted value
1531 58885d79 Iustin Pop

1532 a8083063 Iustin Pop
  """
1533 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
1534 a8083063 Iustin Pop
    return value
1535 a8083063 Iustin Pop
  else:
1536 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
1537 a8083063 Iustin Pop
1538 a8083063 Iustin Pop
1539 a8083063 Iustin Pop
def ShellQuoteArgs(args):
1540 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
1541 58885d79 Iustin Pop

1542 58885d79 Iustin Pop
  @type args: list
1543 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1544 58885d79 Iustin Pop
  @rtype: str
1545 5bbd3f7f Michael Hanselmann
  @return: the quoted arguments concatenated with spaces
1546 a8083063 Iustin Pop

1547 a8083063 Iustin Pop
  """
1548 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
1549 88d14415 Michael Hanselmann
1550 88d14415 Michael Hanselmann
1551 b5b8309d Guido Trotter
def ListVisibleFiles(path):
1552 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1553 58885d79 Iustin Pop

1554 58885d79 Iustin Pop
  @type path: str
1555 58885d79 Iustin Pop
  @param path: the directory to enumerate
1556 58885d79 Iustin Pop
  @rtype: list
1557 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1558 04a69a18 Iustin Pop
  @raise ProgrammerError: if L{path} is not an absolue and normalized path
1559 eedbda4b Michael Hanselmann

1560 eedbda4b Michael Hanselmann
  """
1561 04a69a18 Iustin Pop
  if not IsNormAbsPath(path):
1562 04a69a18 Iustin Pop
    raise errors.ProgrammerError("Path passed to ListVisibleFiles is not"
1563 04a69a18 Iustin Pop
                                 " absolute/normalized: '%s'" % path)
1564 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1565 f3299a07 Michael Hanselmann
  return files
1566 2f8b60b3 Iustin Pop
1567 2f8b60b3 Iustin Pop
1568 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1569 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1570 257f4c0a Iustin Pop

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

1575 2f8b60b3 Iustin Pop
  """
1576 2f8b60b3 Iustin Pop
  try:
1577 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1578 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1579 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1580 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1581 257f4c0a Iustin Pop
    else:
1582 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1583 257f4c0a Iustin Pop
                                   type(user))
1584 2f8b60b3 Iustin Pop
  except KeyError:
1585 2f8b60b3 Iustin Pop
    return default
1586 2f8b60b3 Iustin Pop
  return result.pw_dir
1587 59072e7e Michael Hanselmann
1588 59072e7e Michael Hanselmann
1589 24818e8f Michael Hanselmann
def NewUUID():
1590 59072e7e Michael Hanselmann
  """Returns a random UUID.
1591 59072e7e Michael Hanselmann

1592 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1593 58885d79 Iustin Pop
      filesystem.
1594 58885d79 Iustin Pop
  @rtype: str
1595 58885d79 Iustin Pop

1596 59072e7e Michael Hanselmann
  """
1597 13998ef2 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1598 087b34fe Iustin Pop
1599 087b34fe Iustin Pop
1600 ec2c2bc4 Luca Bigliardi
def GenerateSecret(numbytes=20):
1601 33081d90 Iustin Pop
  """Generates a random secret.
1602 33081d90 Iustin Pop

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

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

1611 33081d90 Iustin Pop
  """
1612 ec2c2bc4 Luca Bigliardi
  return os.urandom(numbytes).encode('hex')
1613 33081d90 Iustin Pop
1614 33081d90 Iustin Pop
1615 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1616 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1617 9dae41ad Guido Trotter

1618 9dae41ad Guido Trotter
  @param dirs: list of tuples (dir_name, dir_mode)
1619 9dae41ad Guido Trotter
  @type dirs: list of (string, integer)
1620 9dae41ad Guido Trotter

1621 9dae41ad Guido Trotter
  """
1622 9dae41ad Guido Trotter
  for dir_name, dir_mode in dirs:
1623 9dae41ad Guido Trotter
    try:
1624 1b2c8f85 Iustin Pop
      os.mkdir(dir_name, dir_mode)
1625 9dae41ad Guido Trotter
    except EnvironmentError, err:
1626 9dae41ad Guido Trotter
      if err.errno != errno.EEXIST:
1627 9dae41ad Guido Trotter
        raise errors.GenericError("Cannot create needed directory"
1628 1b2c8f85 Iustin Pop
                                  " '%s': %s" % (dir_name, err))
1629 b73360e3 Balazs Lecz
    try:
1630 b73360e3 Balazs Lecz
      os.chmod(dir_name, dir_mode)
1631 b73360e3 Balazs Lecz
    except EnvironmentError, err:
1632 b73360e3 Balazs Lecz
      raise errors.GenericError("Cannot change directory permissions on"
1633 b73360e3 Balazs Lecz
                                " '%s': %s" % (dir_name, err))
1634 9dae41ad Guido Trotter
    if not os.path.isdir(dir_name):
1635 9dae41ad Guido Trotter
      raise errors.GenericError("%s is not a directory" % dir_name)
1636 9dae41ad Guido Trotter
1637 9dae41ad Guido Trotter
1638 582ed043 Guido Trotter
def ReadFile(file_name, size=-1):
1639 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1640 ca0aa6d0 Michael Hanselmann

1641 016308cb Iustin Pop
  @type size: int
1642 016308cb Iustin Pop
  @param size: Read at most size bytes (if negative, entire file)
1643 58885d79 Iustin Pop
  @rtype: str
1644 5bbd3f7f Michael Hanselmann
  @return: the (possibly partial) content of the file
1645 ca0aa6d0 Michael Hanselmann

1646 ca0aa6d0 Michael Hanselmann
  """
1647 ca0aa6d0 Michael Hanselmann
  f = open(file_name, "r")
1648 ca0aa6d0 Michael Hanselmann
  try:
1649 582ed043 Guido Trotter
    return f.read(size)
1650 ca0aa6d0 Michael Hanselmann
  finally:
1651 ca0aa6d0 Michael Hanselmann
    f.close()
1652 ca0aa6d0 Michael Hanselmann
1653 ca0aa6d0 Michael Hanselmann
1654 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
1655 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
1656 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
1657 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
1658 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
1659 087b34fe Iustin Pop
  """(Over)write a file atomically.
1660 087b34fe Iustin Pop

1661 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
1662 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
1663 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
1664 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
1665 087b34fe Iustin Pop
  mtime/atime of the file.
1666 087b34fe Iustin Pop

1667 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1668 69efe319 Michael Hanselmann
  target file has the new contents. If the function has raised an
1669 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1670 087b34fe Iustin Pop
  temporary file should be removed.
1671 087b34fe Iustin Pop

1672 58885d79 Iustin Pop
  @type file_name: str
1673 58885d79 Iustin Pop
  @param file_name: the target filename
1674 58885d79 Iustin Pop
  @type fn: callable
1675 58885d79 Iustin Pop
  @param fn: content writing function, called with
1676 58885d79 Iustin Pop
      file descriptor as parameter
1677 69efe319 Michael Hanselmann
  @type data: str
1678 58885d79 Iustin Pop
  @param data: contents of the file
1679 58885d79 Iustin Pop
  @type mode: int
1680 58885d79 Iustin Pop
  @param mode: file mode
1681 58885d79 Iustin Pop
  @type uid: int
1682 58885d79 Iustin Pop
  @param uid: the owner of the file
1683 58885d79 Iustin Pop
  @type gid: int
1684 58885d79 Iustin Pop
  @param gid: the group of the file
1685 58885d79 Iustin Pop
  @type atime: int
1686 58885d79 Iustin Pop
  @param atime: a custom access time to be set on the file
1687 58885d79 Iustin Pop
  @type mtime: int
1688 58885d79 Iustin Pop
  @param mtime: a custom modification time to be set on the file
1689 58885d79 Iustin Pop
  @type close: boolean
1690 58885d79 Iustin Pop
  @param close: whether to close file after writing it
1691 58885d79 Iustin Pop
  @type prewrite: callable
1692 58885d79 Iustin Pop
  @param prewrite: function to be called before writing content
1693 58885d79 Iustin Pop
  @type postwrite: callable
1694 58885d79 Iustin Pop
  @param postwrite: function to be called after writing content
1695 58885d79 Iustin Pop

1696 58885d79 Iustin Pop
  @rtype: None or int
1697 58885d79 Iustin Pop
  @return: None if the 'close' parameter evaluates to True,
1698 58885d79 Iustin Pop
      otherwise the file descriptor
1699 58885d79 Iustin Pop

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

1702 087b34fe Iustin Pop
  """
1703 04a8d789 Michael Hanselmann
  if not os.path.isabs(file_name):
1704 087b34fe Iustin Pop
    raise errors.ProgrammerError("Path passed to WriteFile is not"
1705 087b34fe Iustin Pop
                                 " absolute: '%s'" % file_name)
1706 087b34fe Iustin Pop
1707 087b34fe Iustin Pop
  if [fn, data].count(None) != 1:
1708 087b34fe Iustin Pop
    raise errors.ProgrammerError("fn or data required")
1709 087b34fe Iustin Pop
1710 087b34fe Iustin Pop
  if [atime, mtime].count(None) == 1:
1711 087b34fe Iustin Pop
    raise errors.ProgrammerError("Both atime and mtime must be either"
1712 087b34fe Iustin Pop
                                 " set or None")
1713 087b34fe Iustin Pop
1714 70f4497c Michael Hanselmann
  if backup and not dry_run and os.path.isfile(file_name):
1715 70f4497c Michael Hanselmann
    CreateBackup(file_name)
1716 087b34fe Iustin Pop
1717 087b34fe Iustin Pop
  dir_name, base_name = os.path.split(file_name)
1718 087b34fe Iustin Pop
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
1719 81b7354c Iustin Pop
  do_remove = True
1720 087b34fe Iustin Pop
  # here we need to make sure we remove the temp file, if any error
1721 087b34fe Iustin Pop
  # leaves it in place
1722 087b34fe Iustin Pop
  try:
1723 087b34fe Iustin Pop
    if uid != -1 or gid != -1:
1724 087b34fe Iustin Pop
      os.chown(new_name, uid, gid)
1725 087b34fe Iustin Pop
    if mode:
1726 087b34fe Iustin Pop
      os.chmod(new_name, mode)
1727 71714516 Michael Hanselmann
    if callable(prewrite):
1728 71714516 Michael Hanselmann
      prewrite(fd)
1729 087b34fe Iustin Pop
    if data is not None:
1730 087b34fe Iustin Pop
      os.write(fd, data)
1731 087b34fe Iustin Pop
    else:
1732 087b34fe Iustin Pop
      fn(fd)
1733 71714516 Michael Hanselmann
    if callable(postwrite):
1734 71714516 Michael Hanselmann
      postwrite(fd)
1735 087b34fe Iustin Pop
    os.fsync(fd)
1736 087b34fe Iustin Pop
    if atime is not None and mtime is not None:
1737 087b34fe Iustin Pop
      os.utime(new_name, (atime, mtime))
1738 70f4497c Michael Hanselmann
    if not dry_run:
1739 70f4497c Michael Hanselmann
      os.rename(new_name, file_name)
1740 81b7354c Iustin Pop
      do_remove = False
1741 087b34fe Iustin Pop
  finally:
1742 71714516 Michael Hanselmann
    if close:
1743 71714516 Michael Hanselmann
      os.close(fd)
1744 71714516 Michael Hanselmann
      result = None
1745 71714516 Michael Hanselmann
    else:
1746 71714516 Michael Hanselmann
      result = fd
1747 81b7354c Iustin Pop
    if do_remove:
1748 81b7354c Iustin Pop
      RemoveFile(new_name)
1749 78feb6fb Guido Trotter
1750 71714516 Michael Hanselmann
  return result
1751 71714516 Michael Hanselmann
1752 78feb6fb Guido Trotter
1753 e587b46a Guido Trotter
def ReadOneLineFile(file_name, strict=False):
1754 e587b46a Guido Trotter
  """Return the first non-empty line from a file.
1755 e587b46a Guido Trotter

1756 e587b46a Guido Trotter
  @type strict: boolean
1757 e587b46a Guido Trotter
  @param strict: if True, abort if the file has more than one
1758 e587b46a Guido Trotter
      non-empty line
1759 e587b46a Guido Trotter

1760 e587b46a Guido Trotter
  """
1761 e587b46a Guido Trotter
  file_lines = ReadFile(file_name).splitlines()
1762 e587b46a Guido Trotter
  full_lines = filter(bool, file_lines)
1763 e587b46a Guido Trotter
  if not file_lines or not full_lines:
1764 e587b46a Guido Trotter
    raise errors.GenericError("No data in one-liner file %s" % file_name)
1765 e587b46a Guido Trotter
  elif strict and len(full_lines) > 1:
1766 e587b46a Guido Trotter
    raise errors.GenericError("Too many lines in one-liner file %s" %
1767 e587b46a Guido Trotter
                              file_name)
1768 e587b46a Guido Trotter
  return full_lines[0]
1769 e587b46a Guido Trotter
1770 e587b46a Guido Trotter
1771 7b4126b7 Iustin Pop
def FirstFree(seq, base=0):
1772 7b4126b7 Iustin Pop
  """Returns the first non-existing integer from seq.
1773 7b4126b7 Iustin Pop

1774 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
1775 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
1776 7b4126b7 Iustin Pop
  value, the index will be returned.
1777 7b4126b7 Iustin Pop

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

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

1783 58885d79 Iustin Pop
  @type seq: sequence
1784 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
1785 58885d79 Iustin Pop
  @type base: int
1786 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
1787 58885d79 Iustin Pop
  @rtype: int
1788 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
1789 7b4126b7 Iustin Pop

1790 7b4126b7 Iustin Pop
  """
1791 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
1792 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
1793 7b4126b7 Iustin Pop
    if elem > idx + base:
1794 7b4126b7 Iustin Pop
      # idx is not used
1795 7b4126b7 Iustin Pop
      return idx + base
1796 7b4126b7 Iustin Pop
  return None
1797 7b4126b7 Iustin Pop
1798 7b4126b7 Iustin Pop
1799 dfdc4060 Guido Trotter
def SingleWaitForFdCondition(fdobj, event, timeout):
1800 dcd511c8 Guido Trotter
  """Waits for a condition to occur on the socket.
1801 dcd511c8 Guido Trotter

1802 dfdc4060 Guido Trotter
  Immediately returns at the first interruption.
1803 dfdc4060 Guido Trotter

1804 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
1805 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
1806 dfdc4060 Guido Trotter
  @type event: integer
1807 dcd511c8 Guido Trotter
  @param event: ORed condition (see select module)
1808 dcd511c8 Guido Trotter
  @type timeout: float or None
1809 dcd511c8 Guido Trotter
  @param timeout: Timeout in seconds
1810 dcd511c8 Guido Trotter
  @rtype: int or None
1811 dcd511c8 Guido Trotter
  @return: None for timeout, otherwise occured conditions
1812 dcd511c8 Guido Trotter

1813 dcd511c8 Guido Trotter
  """
1814 dcd511c8 Guido Trotter
  check = (event | select.POLLPRI |
1815 dcd511c8 Guido Trotter
           select.POLLNVAL | select.POLLHUP | select.POLLERR)
1816 dcd511c8 Guido Trotter
1817 dcd511c8 Guido Trotter
  if timeout is not None:
1818 dcd511c8 Guido Trotter
    # Poller object expects milliseconds
1819 dcd511c8 Guido Trotter
    timeout *= 1000
1820 dcd511c8 Guido Trotter
1821 dcd511c8 Guido Trotter
  poller = select.poll()
1822 dfdc4060 Guido Trotter
  poller.register(fdobj, event)
1823 dcd511c8 Guido Trotter
  try:
1824 dfdc4060 Guido Trotter
    # TODO: If the main thread receives a signal and we have no timeout, we
1825 dfdc4060 Guido Trotter
    # could wait forever. This should check a global "quit" flag or something
1826 dfdc4060 Guido Trotter
    # every so often.
1827 dfdc4060 Guido Trotter
    io_events = poller.poll(timeout)
1828 dfdc4060 Guido Trotter
  except select.error, err:
1829 dfdc4060 Guido Trotter
    if err[0] != errno.EINTR:
1830 dfdc4060 Guido Trotter
      raise
1831 dfdc4060 Guido Trotter
    io_events = []
1832 dfdc4060 Guido Trotter
  if io_events and io_events[0][1] & check:
1833 dfdc4060 Guido Trotter
    return io_events[0][1]
1834 dfdc4060 Guido Trotter
  else:
1835 dfdc4060 Guido Trotter
    return None
1836 dfdc4060 Guido Trotter
1837 dfdc4060 Guido Trotter
1838 dfdc4060 Guido Trotter
class FdConditionWaiterHelper(object):
1839 dfdc4060 Guido Trotter
  """Retry helper for WaitForFdCondition.
1840 dfdc4060 Guido Trotter

1841 dfdc4060 Guido Trotter
  This class contains the retried and wait functions that make sure
1842 dfdc4060 Guido Trotter
  WaitForFdCondition can continue waiting until the timeout is actually
1843 dfdc4060 Guido Trotter
  expired.
1844 dfdc4060 Guido Trotter

1845 dfdc4060 Guido Trotter
  """
1846 dfdc4060 Guido Trotter
1847 dfdc4060 Guido Trotter
  def __init__(self, timeout):
1848 dfdc4060 Guido Trotter
    self.timeout = timeout
1849 dfdc4060 Guido Trotter
1850 dfdc4060 Guido Trotter
  def Poll(self, fdobj, event):
1851 dfdc4060 Guido Trotter
    result = SingleWaitForFdCondition(fdobj, event, self.timeout)
1852 dfdc4060 Guido Trotter
    if result is None:
1853 dfdc4060 Guido Trotter
      raise RetryAgain()
1854 dfdc4060 Guido Trotter
    else:
1855 dfdc4060 Guido Trotter
      return result
1856 dfdc4060 Guido Trotter
1857 dfdc4060 Guido Trotter
  def UpdateTimeout(self, timeout):
1858 dfdc4060 Guido Trotter
    self.timeout = timeout
1859 dfdc4060 Guido Trotter
1860 dfdc4060 Guido Trotter
1861 dfdc4060 Guido Trotter
def WaitForFdCondition(fdobj, event, timeout):
1862 dfdc4060 Guido Trotter
  """Waits for a condition to occur on the socket.
1863 dfdc4060 Guido Trotter

1864 dfdc4060 Guido Trotter
  Retries until the timeout is expired, even if interrupted.
1865 dfdc4060 Guido Trotter

1866 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
1867 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
1868 dfdc4060 Guido Trotter
  @type event: integer
1869 dfdc4060 Guido Trotter
  @param event: ORed condition (see select module)
1870 dfdc4060 Guido Trotter
  @type timeout: float or None
1871 dfdc4060 Guido Trotter
  @param timeout: Timeout in seconds
1872 dfdc4060 Guido Trotter
  @rtype: int or None
1873 dfdc4060 Guido Trotter
  @return: None for timeout, otherwise occured conditions
1874 dfdc4060 Guido Trotter

1875 dfdc4060 Guido Trotter
  """
1876 dfdc4060 Guido Trotter
  if timeout is not None:
1877 dfdc4060 Guido Trotter
    retrywaiter = FdConditionWaiterHelper(timeout)
1878 1b429e2a Iustin Pop
    try:
1879 1b429e2a Iustin Pop
      result = Retry(retrywaiter.Poll, RETRY_REMAINING_TIME, timeout,
1880 1b429e2a Iustin Pop
                     args=(fdobj, event), wait_fn=retrywaiter.UpdateTimeout)
1881 1b429e2a Iustin Pop
    except RetryTimeout:
1882 1b429e2a Iustin Pop
      result = None
1883 dfdc4060 Guido Trotter
  else:
1884 dfdc4060 Guido Trotter
    result = None
1885 dfdc4060 Guido Trotter
    while result is None:
1886 dfdc4060 Guido Trotter
      result = SingleWaitForFdCondition(fdobj, event, timeout)
1887 dfdc4060 Guido Trotter
  return result
1888 2de64672 Iustin Pop
1889 2de64672 Iustin Pop
1890 f7414041 Michael Hanselmann
def UniqueSequence(seq):
1891 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
1892 f7414041 Michael Hanselmann

1893 f7414041 Michael Hanselmann
  Element order is preserved.
1894 58885d79 Iustin Pop

1895 58885d79 Iustin Pop
  @type seq: sequence
1896 5bbd3f7f Michael Hanselmann
  @param seq: the sequence with the source elements
1897 58885d79 Iustin Pop
  @rtype: list
1898 58885d79 Iustin Pop
  @return: list of unique elements from seq
1899 58885d79 Iustin Pop

1900 f7414041 Michael Hanselmann
  """
1901 f7414041 Michael Hanselmann
  seen = set()
1902 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
1903 1862d460 Alexander Schreiber
1904 1862d460 Alexander Schreiber
1905 82187135 Renรฉ Nussbaumer
def NormalizeAndValidateMac(mac):
1906 82187135 Renรฉ Nussbaumer
  """Normalizes and check if a MAC address is valid.
1907 1862d460 Alexander Schreiber

1908 5bbd3f7f Michael Hanselmann
  Checks whether the supplied MAC address is formally correct, only
1909 82187135 Renรฉ Nussbaumer
  accepts colon separated format. Normalize it to all lower.
1910 58885d79 Iustin Pop

1911 58885d79 Iustin Pop
  @type mac: str
1912 58885d79 Iustin Pop
  @param mac: the MAC to be validated
1913 82187135 Renรฉ Nussbaumer
  @rtype: str
1914 82187135 Renรฉ Nussbaumer
  @return: returns the normalized and validated MAC.
1915 82187135 Renรฉ Nussbaumer

1916 82187135 Renรฉ Nussbaumer
  @raise errors.OpPrereqError: If the MAC isn't valid
1917 58885d79 Iustin Pop

1918 1862d460 Alexander Schreiber
  """
1919 82187135 Renรฉ Nussbaumer
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$", re.I)
1920 82187135 Renรฉ Nussbaumer
  if not mac_check.match(mac):
1921 82187135 Renรฉ Nussbaumer
    raise errors.OpPrereqError("Invalid MAC address specified: %s" %
1922 82187135 Renรฉ Nussbaumer
                               mac, errors.ECODE_INVAL)
1923 82187135 Renรฉ Nussbaumer
1924 82187135 Renรฉ Nussbaumer
  return mac.lower()
1925 06009e27 Iustin Pop
1926 06009e27 Iustin Pop
1927 06009e27 Iustin Pop
def TestDelay(duration):
1928 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
1929 06009e27 Iustin Pop

1930 58885d79 Iustin Pop
  @type duration: float
1931 58885d79 Iustin Pop
  @param duration: the sleep duration
1932 58885d79 Iustin Pop
  @rtype: boolean
1933 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
1934 58885d79 Iustin Pop

1935 06009e27 Iustin Pop
  """
1936 06009e27 Iustin Pop
  if duration < 0:
1937 38ea42a1 Iustin Pop
    return False, "Invalid sleep duration"
1938 06009e27 Iustin Pop
  time.sleep(duration)
1939 38ea42a1 Iustin Pop
  return True, None
1940 8f765069 Iustin Pop
1941 8f765069 Iustin Pop
1942 7d88772a Iustin Pop
def _CloseFDNoErr(fd, retries=5):
1943 7d88772a Iustin Pop
  """Close a file descriptor ignoring errors.
1944 8f765069 Iustin Pop

1945 7d88772a Iustin Pop
  @type fd: int
1946 7d88772a Iustin Pop
  @param fd: the file descriptor
1947 7d88772a Iustin Pop
  @type retries: int
1948 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
1949 7d88772a Iustin Pop
      other error than EBADF
1950 7d88772a Iustin Pop

1951 7d88772a Iustin Pop
  """
1952 7d88772a Iustin Pop
  try:
1953 7d88772a Iustin Pop
    os.close(fd)
1954 7d88772a Iustin Pop
  except OSError, err:
1955 7d88772a Iustin Pop
    if err.errno != errno.EBADF:
1956 7d88772a Iustin Pop
      if retries > 0:
1957 7d88772a Iustin Pop
        _CloseFDNoErr(fd, retries - 1)
1958 7d88772a Iustin Pop
    # else either it's closed already or we're out of retries, so we
1959 7d88772a Iustin Pop
    # ignore this and go on
1960 7d88772a Iustin Pop
1961 7d88772a Iustin Pop
1962 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
1963 7d88772a Iustin Pop
  """Close file descriptors.
1964 7d88772a Iustin Pop

1965 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
1966 7d88772a Iustin Pop
  stdin/out/err).
1967 8f765069 Iustin Pop

1968 58885d79 Iustin Pop
  @type noclose_fds: list or None
1969 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
1970 58885d79 Iustin Pop
      that should not be closed
1971 58885d79 Iustin Pop

1972 8f765069 Iustin Pop
  """
1973 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
1974 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
1975 8f765069 Iustin Pop
    try:
1976 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
1977 8f765069 Iustin Pop
      if MAXFD < 0:
1978 8f765069 Iustin Pop
        MAXFD = 1024
1979 8f765069 Iustin Pop
    except OSError:
1980 8f765069 Iustin Pop
      MAXFD = 1024
1981 8f765069 Iustin Pop
  else:
1982 8f765069 Iustin Pop
    MAXFD = 1024
1983 7d88772a Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1984 7d88772a Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
1985 7d88772a Iustin Pop
    maxfd = MAXFD
1986 7d88772a Iustin Pop
1987 7d88772a Iustin Pop
  # Iterate through and close all file descriptors (except the standard ones)
1988 7d88772a Iustin Pop
  for fd in range(3, maxfd):
1989 7d88772a Iustin Pop
    if noclose_fds and fd in noclose_fds:
1990 7d88772a Iustin Pop
      continue
1991 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1992 7d88772a Iustin Pop
1993 7d88772a Iustin Pop
1994 4c32a8bd Luca Bigliardi
def Mlockall(_ctypes=ctypes):
1995 4b6fa0bf Luca Bigliardi
  """Lock current process' virtual address space into RAM.
1996 4b6fa0bf Luca Bigliardi

1997 4b6fa0bf Luca Bigliardi
  This is equivalent to the C call mlockall(MCL_CURRENT|MCL_FUTURE),
1998 4b6fa0bf Luca Bigliardi
  see mlock(2) for more details. This function requires ctypes module.
1999 4b6fa0bf Luca Bigliardi

2000 4c32a8bd Luca Bigliardi
  @raises errors.NoCtypesError: if ctypes module is not found
2001 4c32a8bd Luca Bigliardi

2002 4b6fa0bf Luca Bigliardi
  """
2003 4c32a8bd Luca Bigliardi
  if _ctypes is None:
2004 4c32a8bd Luca Bigliardi
    raise errors.NoCtypesError()
2005 4b6fa0bf Luca Bigliardi
2006 4c32a8bd Luca Bigliardi
  libc = _ctypes.cdll.LoadLibrary("libc.so.6")
2007 4b6fa0bf Luca Bigliardi
  if libc is None:
2008 4b6fa0bf Luca Bigliardi
    logging.error("Cannot set memory lock, ctypes cannot load libc")
2009 4b6fa0bf Luca Bigliardi
    return
2010 4b6fa0bf Luca Bigliardi
2011 4b6fa0bf Luca Bigliardi
  # Some older version of the ctypes module don't have built-in functionality
2012 4b6fa0bf Luca Bigliardi
  # to access the errno global variable, where function error codes are stored.
2013 4b6fa0bf Luca Bigliardi
  # By declaring this variable as a pointer to an integer we can then access
2014 4b6fa0bf Luca Bigliardi
  # its value correctly, should the mlockall call fail, in order to see what
2015 4b6fa0bf Luca Bigliardi
  # the actual error code was.
2016 20601361 Luca Bigliardi
  # pylint: disable-msg=W0212
2017 4c32a8bd Luca Bigliardi
  libc.__errno_location.restype = _ctypes.POINTER(_ctypes.c_int)
2018 4b6fa0bf Luca Bigliardi
2019 4b6fa0bf Luca Bigliardi
  if libc.mlockall(_MCL_CURRENT | _MCL_FUTURE):
2020 20601361 Luca Bigliardi
    # pylint: disable-msg=W0212
2021 6ed0bbce Luca Bigliardi
    logging.error("Cannot set memory lock: %s",
2022 4b6fa0bf Luca Bigliardi
                  os.strerror(libc.__errno_location().contents.value))
2023 4b6fa0bf Luca Bigliardi
    return
2024 4b6fa0bf Luca Bigliardi
2025 4b6fa0bf Luca Bigliardi
  logging.debug("Memory lock set")
2026 4b6fa0bf Luca Bigliardi
2027 4b6fa0bf Luca Bigliardi
2028 743b53d4 Renรฉ Nussbaumer
def Daemonize(logfile, run_uid, run_gid):
2029 7d88772a Iustin Pop
  """Daemonize the current process.
2030 7d88772a Iustin Pop

2031 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
2032 7d88772a Iustin Pop
  runs it in the background as a daemon.
2033 7d88772a Iustin Pop

2034 7d88772a Iustin Pop
  @type logfile: str
2035 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
2036 743b53d4 Renรฉ Nussbaumer
  @type run_uid: int
2037 743b53d4 Renรฉ Nussbaumer
  @param run_uid: Run the child under this uid
2038 743b53d4 Renรฉ Nussbaumer
  @type run_gid: int
2039 743b53d4 Renรฉ Nussbaumer
  @param run_gid: Run the child under this gid
2040 7d88772a Iustin Pop
  @rtype: int
2041 5fcc718f Iustin Pop
  @return: the value zero
2042 7d88772a Iustin Pop

2043 7d88772a Iustin Pop
  """
2044 7260cfbe Iustin Pop
  # pylint: disable-msg=W0212
2045 7260cfbe Iustin Pop
  # yes, we really want os._exit
2046 7d88772a Iustin Pop
  UMASK = 077
2047 7d88772a Iustin Pop
  WORKDIR = "/"
2048 8f765069 Iustin Pop
2049 8f765069 Iustin Pop
  # this might fail
2050 8f765069 Iustin Pop
  pid = os.fork()
2051 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
2052 8f765069 Iustin Pop
    os.setsid()
2053 743b53d4 Renรฉ Nussbaumer
    # FIXME: When removing again and moving to start-stop-daemon privilege drop
2054 743b53d4 Renรฉ Nussbaumer
    #        make sure to check for config permission and bail out when invoked
2055 743b53d4 Renรฉ Nussbaumer
    #        with wrong user.
2056 743b53d4 Renรฉ Nussbaumer
    os.setgid(run_gid)
2057 743b53d4 Renรฉ Nussbaumer
    os.setuid(run_uid)
2058 8f765069 Iustin Pop
    # this might fail
2059 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
2060 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
2061 8f765069 Iustin Pop
      os.chdir(WORKDIR)
2062 8f765069 Iustin Pop
      os.umask(UMASK)
2063 8f765069 Iustin Pop
    else:
2064 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
2065 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
2066 8f765069 Iustin Pop
  else:
2067 8f765069 Iustin Pop
    os._exit(0) # Exit parent of the first child.
2068 8f765069 Iustin Pop
2069 7d88772a Iustin Pop
  for fd in range(3):
2070 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
2071 7d88772a Iustin Pop
  i = os.open("/dev/null", os.O_RDONLY) # stdin
2072 7d88772a Iustin Pop
  assert i == 0, "Can't close/reopen stdin"
2073 7d88772a Iustin Pop
  i = os.open(logfile, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0600) # stdout
2074 7d88772a Iustin Pop
  assert i == 1, "Can't close/reopen stdout"
2075 7d88772a Iustin Pop
  # Duplicate standard output to standard error.
2076 7d88772a Iustin Pop
  os.dup2(1, 2)
2077 8f765069 Iustin Pop
  return 0
2078 57c177af Iustin Pop
2079 57c177af Iustin Pop
2080 53beffbb Iustin Pop
def DaemonPidFileName(name):
2081 58885d79 Iustin Pop
  """Compute a ganeti pid file absolute path
2082 58885d79 Iustin Pop

2083 58885d79 Iustin Pop
  @type name: str
2084 58885d79 Iustin Pop
  @param name: the daemon name
2085 58885d79 Iustin Pop
  @rtype: str
2086 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
2087 58885d79 Iustin Pop
      daemon name
2088 b330ac0b Guido Trotter

2089 b330ac0b Guido Trotter
  """
2090 c4feafe8 Iustin Pop
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
2091 b330ac0b Guido Trotter
2092 b330ac0b Guido Trotter
2093 2826b361 Guido Trotter
def EnsureDaemon(name):
2094 2826b361 Guido Trotter
  """Check for and start daemon if not alive.
2095 2826b361 Guido Trotter

2096 2826b361 Guido Trotter
  """
2097 2826b361 Guido Trotter
  result = RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
2098 2826b361 Guido Trotter
  if result.failed:
2099 2826b361 Guido Trotter
    logging.error("Can't start daemon '%s', failure %s, output: %s",
2100 2826b361 Guido Trotter
                  name, result.fail_reason, result.output)
2101 2826b361 Guido Trotter
    return False
2102 2826b361 Guido Trotter
2103 2826b361 Guido Trotter
  return True
2104 b330ac0b Guido Trotter
2105 b330ac0b Guido Trotter
2106 db147305 Tom Limoncelli
def StopDaemon(name):
2107 db147305 Tom Limoncelli
  """Stop daemon
2108 db147305 Tom Limoncelli

2109 db147305 Tom Limoncelli
  """
2110 db147305 Tom Limoncelli
  result = RunCmd([constants.DAEMON_UTIL, "stop", name])
2111 db147305 Tom Limoncelli
  if result.failed:
2112 db147305 Tom Limoncelli
    logging.error("Can't stop daemon '%s', failure %s, output: %s",
2113 db147305 Tom Limoncelli
                  name, result.fail_reason, result.output)
2114 db147305 Tom Limoncelli
    return False
2115 db147305 Tom Limoncelli
2116 db147305 Tom Limoncelli
  return True
2117 db147305 Tom Limoncelli
2118 db147305 Tom Limoncelli
2119 b330ac0b Guido Trotter
def WritePidFile(name):
2120 b330ac0b Guido Trotter
  """Write the current process pidfile.
2121 b330ac0b Guido Trotter

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

2124 58885d79 Iustin Pop
  @type name: str
2125 58885d79 Iustin Pop
  @param name: the daemon name to use
2126 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
2127 58885d79 Iustin Pop
      points to a live process
2128 b330ac0b Guido Trotter

2129 b330ac0b Guido Trotter
  """
2130 b330ac0b Guido Trotter
  pid = os.getpid()
2131 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
2132 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
2133 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
2134 b330ac0b Guido Trotter
2135 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
2136 b330ac0b Guido Trotter
2137 b330ac0b Guido Trotter
2138 b330ac0b Guido Trotter
def RemovePidFile(name):
2139 b330ac0b Guido Trotter
  """Remove the current process pidfile.
2140 b330ac0b Guido Trotter

2141 b330ac0b Guido Trotter
  Any errors are ignored.
2142 b330ac0b Guido Trotter

2143 58885d79 Iustin Pop
  @type name: str
2144 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
2145 58885d79 Iustin Pop

2146 b330ac0b Guido Trotter
  """
2147 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
2148 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
2149 b330ac0b Guido Trotter
  try:
2150 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
2151 7260cfbe Iustin Pop
  except: # pylint: disable-msg=W0702
2152 b330ac0b Guido Trotter
    pass
2153 b330ac0b Guido Trotter
2154 b330ac0b Guido Trotter
2155 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
2156 ff5251bc Iustin Pop
                waitpid=False):
2157 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
2158 b2a1f511 Iustin Pop

2159 b2a1f511 Iustin Pop
  @type pid: int
2160 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
2161 38206f3c Iustin Pop
  @type signal_: int
2162 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
2163 b2a1f511 Iustin Pop
  @type timeout: int
2164 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
2165 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
2166 b2a1f511 Iustin Pop
                  will be done
2167 ff5251bc Iustin Pop
  @type waitpid: boolean
2168 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
2169 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
2170 ff5251bc Iustin Pop
      would remain as zombie
2171 b2a1f511 Iustin Pop

2172 b2a1f511 Iustin Pop
  """
2173 ff5251bc Iustin Pop
  def _helper(pid, signal_, wait):
2174 ff5251bc Iustin Pop
    """Simple helper to encapsulate the kill/waitpid sequence"""
2175 560cbec1 Michael Hanselmann
    if IgnoreProcessNotFound(os.kill, pid, signal_) and wait:
2176 ff5251bc Iustin Pop
      try:
2177 ff5251bc Iustin Pop
        os.waitpid(pid, os.WNOHANG)
2178 ff5251bc Iustin Pop
      except OSError:
2179 ff5251bc Iustin Pop
        pass
2180 ff5251bc Iustin Pop
2181 b2a1f511 Iustin Pop
  if pid <= 0:
2182 b2a1f511 Iustin Pop
    # kill with pid=0 == suicide
2183 b2a1f511 Iustin Pop
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
2184 b2a1f511 Iustin Pop
2185 b2a1f511 Iustin Pop
  if not IsProcessAlive(pid):
2186 b2a1f511 Iustin Pop
    return
2187 31892b4c Michael Hanselmann
2188 ff5251bc Iustin Pop
  _helper(pid, signal_, waitpid)
2189 31892b4c Michael Hanselmann
2190 b2a1f511 Iustin Pop
  if timeout <= 0:
2191 b2a1f511 Iustin Pop
    return
2192 7167159a Michael Hanselmann
2193 31892b4c Michael Hanselmann
  def _CheckProcess():
2194 31892b4c Michael Hanselmann
    if not IsProcessAlive(pid):
2195 31892b4c Michael Hanselmann
      return
2196 31892b4c Michael Hanselmann
2197 7167159a Michael Hanselmann
    try:
2198 7167159a Michael Hanselmann
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
2199 7167159a Michael Hanselmann
    except OSError:
2200 31892b4c Michael Hanselmann
      raise RetryAgain()
2201 31892b4c Michael Hanselmann
2202 31892b4c Michael Hanselmann
    if result_pid > 0:
2203 31892b4c Michael Hanselmann
      return
2204 31892b4c Michael Hanselmann
2205 31892b4c Michael Hanselmann
    raise RetryAgain()
2206 31892b4c Michael Hanselmann
2207 31892b4c Michael Hanselmann
  try:
2208 31892b4c Michael Hanselmann
    # Wait up to $timeout seconds
2209 31892b4c Michael Hanselmann
    Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
2210 31892b4c Michael Hanselmann
  except RetryTimeout:
2211 31892b4c Michael Hanselmann
    pass
2212 7167159a Michael Hanselmann
2213 b2a1f511 Iustin Pop
  if IsProcessAlive(pid):
2214 7167159a Michael Hanselmann
    # Kill process if it's still alive
2215 e1bd0072 Iustin Pop
    _helper(pid, signal.SIGKILL, waitpid)
2216 b2a1f511 Iustin Pop
2217 b2a1f511 Iustin Pop
2218 57c177af Iustin Pop
def FindFile(name, search_path, test=os.path.exists):
2219 57c177af Iustin Pop
  """Look for a filesystem object in a given path.
2220 57c177af Iustin Pop

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

2224 58885d79 Iustin Pop
  @type name: str
2225 58885d79 Iustin Pop
  @param name: the name to look for
2226 58885d79 Iustin Pop
  @type search_path: str
2227 58885d79 Iustin Pop
  @param search_path: location to start at
2228 58885d79 Iustin Pop
  @type test: callable
2229 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
2230 58885d79 Iustin Pop
      if the a given object is valid; the default value is
2231 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
2232 58885d79 Iustin Pop
  @rtype: str or None
2233 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
2234 57c177af Iustin Pop

2235 57c177af Iustin Pop
  """
2236 f95c81bf Iustin Pop
  # validate the filename mask
2237 f95c81bf Iustin Pop
  if constants.EXT_PLUGIN_MASK.match(name) is None:
2238 f95c81bf Iustin Pop
    logging.critical("Invalid value passed for external script name: '%s'",
2239 f95c81bf Iustin Pop
                     name)
2240 f95c81bf Iustin Pop
    return None
2241 f95c81bf Iustin Pop
2242 57c177af Iustin Pop
  for dir_name in search_path:
2243 e02b9114 Iustin Pop
    # FIXME: investigate switch to PathJoin
2244 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
2245 f95c81bf Iustin Pop
    # check the user test and that we're indeed resolving to the given
2246 f95c81bf Iustin Pop
    # basename
2247 f95c81bf Iustin Pop
    if test(item_name) and os.path.basename(item_name) == name:
2248 57c177af Iustin Pop
      return item_name
2249 57c177af Iustin Pop
  return None
2250 8d1a2a64 Michael Hanselmann
2251 8d1a2a64 Michael Hanselmann
2252 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
2253 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
2254 8d1a2a64 Michael Hanselmann

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

2258 58885d79 Iustin Pop
  @type vglist: dict
2259 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
2260 58885d79 Iustin Pop
  @type vgname: str
2261 58885d79 Iustin Pop
  @param vgname: the volume group we should check
2262 58885d79 Iustin Pop
  @type minsize: int
2263 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
2264 58885d79 Iustin Pop
  @rtype: None or str
2265 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
2266 8d1a2a64 Michael Hanselmann

2267 8d1a2a64 Michael Hanselmann
  """
2268 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
2269 8d1a2a64 Michael Hanselmann
  if vgsize is None:
2270 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
2271 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
2272 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
2273 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
2274 8d1a2a64 Michael Hanselmann
  return None
2275 7996a135 Iustin Pop
2276 7996a135 Iustin Pop
2277 45bc5e4a Michael Hanselmann
def SplitTime(value):
2278 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
2279 739be818 Michael Hanselmann

2280 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
2281 45bc5e4a Michael Hanselmann
  @type value: int or float
2282 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
2283 739be818 Michael Hanselmann

2284 739be818 Michael Hanselmann
  """
2285 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
2286 45bc5e4a Michael Hanselmann
2287 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
2288 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
2289 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
2290 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
2291 45bc5e4a Michael Hanselmann
2292 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
2293 739be818 Michael Hanselmann
2294 739be818 Michael Hanselmann
2295 739be818 Michael Hanselmann
def MergeTime(timetuple):
2296 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
2297 739be818 Michael Hanselmann

2298 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
2299 739be818 Michael Hanselmann
  @type timetuple: tuple
2300 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
2301 739be818 Michael Hanselmann

2302 739be818 Michael Hanselmann
  """
2303 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
2304 739be818 Michael Hanselmann
2305 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
2306 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
2307 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
2308 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
2309 739be818 Michael Hanselmann
2310 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
2311 739be818 Michael Hanselmann
2312 739be818 Michael Hanselmann
2313 de3b8e39 Luca Bigliardi
class LogFileHandler(logging.FileHandler):
2314 de3b8e39 Luca Bigliardi
  """Log handler that doesn't fallback to stderr.
2315 de3b8e39 Luca Bigliardi

2316 de3b8e39 Luca Bigliardi
  When an error occurs while writing on the logfile, logging.FileHandler tries
2317 de3b8e39 Luca Bigliardi
  to log on stderr. This doesn't work in ganeti since stderr is redirected to
2318 de3b8e39 Luca Bigliardi
  the logfile. This class avoids failures reporting errors to /dev/console.
2319 de3b8e39 Luca Bigliardi

2320 de3b8e39 Luca Bigliardi
  """
2321 de3b8e39 Luca Bigliardi
  def __init__(self, filename, mode="a", encoding=None):
2322 de3b8e39 Luca Bigliardi
    """Open the specified file and use it as the stream for logging.
2323 de3b8e39 Luca Bigliardi

2324 de3b8e39 Luca Bigliardi
    Also open /dev/console to report errors while logging.
2325 de3b8e39 Luca Bigliardi

2326 de3b8e39 Luca Bigliardi
    """
2327 de3b8e39 Luca Bigliardi
    logging.FileHandler.__init__(self, filename, mode, encoding)
2328 de3b8e39 Luca Bigliardi
    self.console = open(constants.DEV_CONSOLE, "a")
2329 de3b8e39 Luca Bigliardi
2330 20601361 Luca Bigliardi
  def handleError(self, record): # pylint: disable-msg=C0103
2331 de3b8e39 Luca Bigliardi
    """Handle errors which occur during an emit() call.
2332 de3b8e39 Luca Bigliardi

2333 de3b8e39 Luca Bigliardi
    Try to handle errors with FileHandler method, if it fails write to
2334 de3b8e39 Luca Bigliardi
    /dev/console.
2335 de3b8e39 Luca Bigliardi

2336 de3b8e39 Luca Bigliardi
    """
2337 de3b8e39 Luca Bigliardi
    try:
2338 05b35f15 Luca Bigliardi
      logging.FileHandler.handleError(self, record)
2339 20601361 Luca Bigliardi
    except Exception: # pylint: disable-msg=W0703
2340 de3b8e39 Luca Bigliardi
      try:
2341 de3b8e39 Luca Bigliardi
        self.console.write("Cannot log message:\n%s\n" % self.format(record))
2342 20601361 Luca Bigliardi
      except Exception: # pylint: disable-msg=W0703
2343 de3b8e39 Luca Bigliardi
        # Log handler tried everything it could, now just give up
2344 de3b8e39 Luca Bigliardi
        pass
2345 de3b8e39 Luca Bigliardi
2346 de3b8e39 Luca Bigliardi
2347 ea34193f Iustin Pop
def SetupLogging(logfile, debug=0, stderr_logging=False, program="",
2348 49e60a28 Luca Bigliardi
                 multithreaded=False, syslog=constants.SYSLOG_USAGE,
2349 49e60a28 Luca Bigliardi
                 console_logging=False):
2350 82d9caef Iustin Pop
  """Configures the logging module.
2351 82d9caef Iustin Pop

2352 58885d79 Iustin Pop
  @type logfile: str
2353 58885d79 Iustin Pop
  @param logfile: the filename to which we should log
2354 ea34193f Iustin Pop
  @type debug: integer
2355 ea34193f Iustin Pop
  @param debug: if greater than zero, enable debug messages, otherwise
2356 58885d79 Iustin Pop
      only those at C{INFO} and above level
2357 58885d79 Iustin Pop
  @type stderr_logging: boolean
2358 58885d79 Iustin Pop
  @param stderr_logging: whether we should also log to the standard error
2359 58885d79 Iustin Pop
  @type program: str
2360 58885d79 Iustin Pop
  @param program: the name under which we should log messages
2361 d21d09d6 Iustin Pop
  @type multithreaded: boolean
2362 d21d09d6 Iustin Pop
  @param multithreaded: if True, will add the thread name to the log file
2363 551b6283 Iustin Pop
  @type syslog: string
2364 551b6283 Iustin Pop
  @param syslog: one of 'no', 'yes', 'only':
2365 551b6283 Iustin Pop
      - if no, syslog is not used
2366 551b6283 Iustin Pop
      - if yes, syslog is used (in addition to file-logging)
2367 551b6283 Iustin Pop
      - if only, only syslog is used
2368 49e60a28 Luca Bigliardi
  @type console_logging: boolean
2369 49e60a28 Luca Bigliardi
  @param console_logging: if True, will use a FileHandler which falls back to
2370 49e60a28 Luca Bigliardi
      the system console if logging fails
2371 58885d79 Iustin Pop
  @raise EnvironmentError: if we can't open the log file and
2372 551b6283 Iustin Pop
      syslog/stderr logging is disabled
2373 58885d79 Iustin Pop

2374 82d9caef Iustin Pop
  """
2375 d21d09d6 Iustin Pop
  fmt = "%(asctime)s: " + program + " pid=%(process)d"
2376 551b6283 Iustin Pop
  sft = program + "[%(process)d]:"
2377 d21d09d6 Iustin Pop
  if multithreaded:
2378 d21d09d6 Iustin Pop
    fmt += "/%(threadName)s"
2379 551b6283 Iustin Pop
    sft += " (%(threadName)s)"
2380 82d9caef Iustin Pop
  if debug:
2381 d21d09d6 Iustin Pop
    fmt += " %(module)s:%(lineno)s"
2382 551b6283 Iustin Pop
    # no debug info for syslog loggers
2383 d21d09d6 Iustin Pop
  fmt += " %(levelname)s %(message)s"
2384 551b6283 Iustin Pop
  # yes, we do want the textual level, as remote syslog will probably
2385 551b6283 Iustin Pop
  # lose the error level, and it's easier to grep for it
2386 551b6283 Iustin Pop
  sft += " %(levelname)s %(message)s"
2387 82d9caef Iustin Pop
  formatter = logging.Formatter(fmt)
2388 551b6283 Iustin Pop
  sys_fmt = logging.Formatter(sft)
2389 82d9caef Iustin Pop
2390 82d9caef Iustin Pop
  root_logger = logging.getLogger("")
2391 82d9caef Iustin Pop
  root_logger.setLevel(logging.NOTSET)
2392 82d9caef Iustin Pop
2393 6346a9e5 Michael Hanselmann
  # Remove all previously setup handlers
2394 6346a9e5 Michael Hanselmann
  for handler in root_logger.handlers:
2395 7d88772a Iustin Pop
    handler.close()
2396 6346a9e5 Michael Hanselmann
    root_logger.removeHandler(handler)
2397 6346a9e5 Michael Hanselmann
2398 82d9caef Iustin Pop
  if stderr_logging:
2399 82d9caef Iustin Pop
    stderr_handler = logging.StreamHandler()
2400 82d9caef Iustin Pop
    stderr_handler.setFormatter(formatter)
2401 82d9caef Iustin Pop
    if debug:
2402 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.NOTSET)
2403 82d9caef Iustin Pop
    else:
2404 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.CRITICAL)
2405 82d9caef Iustin Pop
    root_logger.addHandler(stderr_handler)
2406 82d9caef Iustin Pop
2407 551b6283 Iustin Pop
  if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY):
2408 551b6283 Iustin Pop
    facility = logging.handlers.SysLogHandler.LOG_DAEMON
2409 551b6283 Iustin Pop
    syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET,
2410 551b6283 Iustin Pop
                                                    facility)
2411 551b6283 Iustin Pop
    syslog_handler.setFormatter(sys_fmt)
2412 551b6283 Iustin Pop
    # Never enable debug over syslog
2413 551b6283 Iustin Pop
    syslog_handler.setLevel(logging.INFO)
2414 551b6283 Iustin Pop
    root_logger.addHandler(syslog_handler)
2415 551b6283 Iustin Pop
2416 551b6283 Iustin Pop
  if syslog != constants.SYSLOG_ONLY:
2417 551b6283 Iustin Pop
    # this can fail, if the logging directories are not setup or we have
2418 551b6283 Iustin Pop
    # a permisssion problem; in this case, it's best to log but ignore
2419 551b6283 Iustin Pop
    # the error if stderr_logging is True, and if false we re-raise the
2420 551b6283 Iustin Pop
    # exception since otherwise we could run but without any logs at all
2421 551b6283 Iustin Pop
    try:
2422 49e60a28 Luca Bigliardi
      if console_logging:
2423 49e60a28 Luca Bigliardi
        logfile_handler = LogFileHandler(logfile)
2424 49e60a28 Luca Bigliardi
      else:
2425 49e60a28 Luca Bigliardi
        logfile_handler = logging.FileHandler(logfile)
2426 551b6283 Iustin Pop
      logfile_handler.setFormatter(formatter)
2427 551b6283 Iustin Pop
      if debug:
2428 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.DEBUG)
2429 551b6283 Iustin Pop
      else:
2430 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.INFO)
2431 551b6283 Iustin Pop
      root_logger.addHandler(logfile_handler)
2432 551b6283 Iustin Pop
    except EnvironmentError:
2433 551b6283 Iustin Pop
      if stderr_logging or syslog == constants.SYSLOG_YES:
2434 551b6283 Iustin Pop
        logging.exception("Failed to enable logging to file '%s'", logfile)
2435 551b6283 Iustin Pop
      else:
2436 551b6283 Iustin Pop
        # we need to re-raise the exception
2437 551b6283 Iustin Pop
        raise
2438 82d9caef Iustin Pop
2439 016d04b3 Michael Hanselmann
2440 da961187 Guido Trotter
def IsNormAbsPath(path):
2441 17c61836 Guido Trotter
  """Check whether a path is absolute and also normalized
2442 da961187 Guido Trotter

2443 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
2444 da961187 Guido Trotter

2445 da961187 Guido Trotter
  """
2446 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
2447 82d9caef Iustin Pop
2448 016d04b3 Michael Hanselmann
2449 4bb678e9 Iustin Pop
def PathJoin(*args):
2450 4bb678e9 Iustin Pop
  """Safe-join a list of path components.
2451 4bb678e9 Iustin Pop

2452 4bb678e9 Iustin Pop
  Requirements:
2453 4bb678e9 Iustin Pop
      - the first argument must be an absolute path
2454 4bb678e9 Iustin Pop
      - no component in the path must have backtracking (e.g. /../),
2455 4bb678e9 Iustin Pop
        since we check for normalization at the end
2456 4bb678e9 Iustin Pop

2457 4bb678e9 Iustin Pop
  @param args: the path components to be joined
2458 4bb678e9 Iustin Pop
  @raise ValueError: for invalid paths
2459 4bb678e9 Iustin Pop

2460 4bb678e9 Iustin Pop
  """
2461 4bb678e9 Iustin Pop
  # ensure we're having at least one path passed in
2462 4bb678e9 Iustin Pop
  assert args
2463 4bb678e9 Iustin Pop
  # ensure the first component is an absolute and normalized path name
2464 4bb678e9 Iustin Pop
  root = args[0]
2465 4bb678e9 Iustin Pop
  if not IsNormAbsPath(root):
2466 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
2467 4bb678e9 Iustin Pop
  result = os.path.join(*args)
2468 4bb678e9 Iustin Pop
  # ensure that the whole path is normalized
2469 4bb678e9 Iustin Pop
  if not IsNormAbsPath(result):
2470 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
2471 4bb678e9 Iustin Pop
  # check that we're still under the original prefix
2472 4bb678e9 Iustin Pop
  prefix = os.path.commonprefix([root, result])
2473 4bb678e9 Iustin Pop
  if prefix != root:
2474 4bb678e9 Iustin Pop
    raise ValueError("Error: path joining resulted in different prefix"
2475 4bb678e9 Iustin Pop
                     " (%s != %s)" % (prefix, root))
2476 4bb678e9 Iustin Pop
  return result
2477 4bb678e9 Iustin Pop
2478 4bb678e9 Iustin Pop
2479 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
2480 f65f63ef Iustin Pop
  """Return the last lines from a file.
2481 f65f63ef Iustin Pop

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

2486 f65f63ef Iustin Pop
  @param fname: the file name
2487 f65f63ef Iustin Pop
  @type lines: int
2488 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
2489 f65f63ef Iustin Pop

2490 f65f63ef Iustin Pop
  """
2491 f65f63ef Iustin Pop
  fd = open(fname, "r")
2492 f65f63ef Iustin Pop
  try:
2493 f65f63ef Iustin Pop
    fd.seek(0, 2)
2494 f65f63ef Iustin Pop
    pos = fd.tell()
2495 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
2496 f65f63ef Iustin Pop
    fd.seek(pos, 0)
2497 f65f63ef Iustin Pop
    raw_data = fd.read()
2498 f65f63ef Iustin Pop
  finally:
2499 f65f63ef Iustin Pop
    fd.close()
2500 f65f63ef Iustin Pop
2501 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
2502 f65f63ef Iustin Pop
  return rows[-lines:]
2503 f65f63ef Iustin Pop
2504 f65f63ef Iustin Pop
2505 24d70417 Michael Hanselmann
def FormatTimestampWithTZ(secs):
2506 24d70417 Michael Hanselmann
  """Formats a Unix timestamp with the local timezone.
2507 24d70417 Michael Hanselmann

2508 24d70417 Michael Hanselmann
  """
2509 24d70417 Michael Hanselmann
  return time.strftime("%F %T %Z", time.gmtime(secs))
2510 24d70417 Michael Hanselmann
2511 24d70417 Michael Hanselmann
2512 27e46076 Michael Hanselmann
def _ParseAsn1Generalizedtime(value):
2513 27e46076 Michael Hanselmann
  """Parses an ASN1 GENERALIZEDTIME timestamp as used by pyOpenSSL.
2514 27e46076 Michael Hanselmann

2515 27e46076 Michael Hanselmann
  @type value: string
2516 27e46076 Michael Hanselmann
  @param value: ASN1 GENERALIZEDTIME timestamp
2517 27e46076 Michael Hanselmann

2518 27e46076 Michael Hanselmann
  """
2519 27e46076 Michael Hanselmann
  m = re.match(r"^(\d+)([-+]\d\d)(\d\d)$", value)
2520 27e46076 Michael Hanselmann
  if m:
2521 27e46076 Michael Hanselmann
    # We have an offset
2522 27e46076 Michael Hanselmann
    asn1time = m.group(1)
2523 27e46076 Michael Hanselmann
    hours = int(m.group(2))
2524 27e46076 Michael Hanselmann
    minutes = int(m.group(3))
2525 27e46076 Michael Hanselmann
    utcoffset = (60 * hours) + minutes
2526 27e46076 Michael Hanselmann
  else:
2527 27e46076 Michael Hanselmann
    if not value.endswith("Z"):
2528 27e46076 Michael Hanselmann
      raise ValueError("Missing timezone")
2529 27e46076 Michael Hanselmann
    asn1time = value[:-1]
2530 27e46076 Michael Hanselmann
    utcoffset = 0
2531 27e46076 Michael Hanselmann
2532 27e46076 Michael Hanselmann
  parsed = time.strptime(asn1time, "%Y%m%d%H%M%S")
2533 27e46076 Michael Hanselmann
2534 27e46076 Michael Hanselmann
  tt = datetime.datetime(*(parsed[:7])) - datetime.timedelta(minutes=utcoffset)
2535 27e46076 Michael Hanselmann
2536 27e46076 Michael Hanselmann
  return calendar.timegm(tt.utctimetuple())
2537 27e46076 Michael Hanselmann
2538 27e46076 Michael Hanselmann
2539 27e46076 Michael Hanselmann
def GetX509CertValidity(cert):
2540 27e46076 Michael Hanselmann
  """Returns the validity period of the certificate.
2541 27e46076 Michael Hanselmann

2542 27e46076 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2543 27e46076 Michael Hanselmann
  @param cert: X509 certificate object
2544 27e46076 Michael Hanselmann

2545 27e46076 Michael Hanselmann
  """
2546 27e46076 Michael Hanselmann
  # The get_notBefore and get_notAfter functions are only supported in
2547 27e46076 Michael Hanselmann
  # pyOpenSSL 0.7 and above.
2548 27e46076 Michael Hanselmann
  try:
2549 27e46076 Michael Hanselmann
    get_notbefore_fn = cert.get_notBefore
2550 27e46076 Michael Hanselmann
  except AttributeError:
2551 27e46076 Michael Hanselmann
    not_before = None
2552 27e46076 Michael Hanselmann
  else:
2553 27e46076 Michael Hanselmann
    not_before_asn1 = get_notbefore_fn()
2554 27e46076 Michael Hanselmann
2555 27e46076 Michael Hanselmann
    if not_before_asn1 is None:
2556 27e46076 Michael Hanselmann
      not_before = None
2557 27e46076 Michael Hanselmann
    else:
2558 27e46076 Michael Hanselmann
      not_before = _ParseAsn1Generalizedtime(not_before_asn1)
2559 27e46076 Michael Hanselmann
2560 27e46076 Michael Hanselmann
  try:
2561 27e46076 Michael Hanselmann
    get_notafter_fn = cert.get_notAfter
2562 27e46076 Michael Hanselmann
  except AttributeError:
2563 27e46076 Michael Hanselmann
    not_after = None
2564 27e46076 Michael Hanselmann
  else:
2565 27e46076 Michael Hanselmann
    not_after_asn1 = get_notafter_fn()
2566 27e46076 Michael Hanselmann
2567 27e46076 Michael Hanselmann
    if not_after_asn1 is None:
2568 27e46076 Michael Hanselmann
      not_after = None
2569 27e46076 Michael Hanselmann
    else:
2570 27e46076 Michael Hanselmann
      not_after = _ParseAsn1Generalizedtime(not_after_asn1)
2571 27e46076 Michael Hanselmann
2572 27e46076 Michael Hanselmann
  return (not_before, not_after)
2573 27e46076 Michael Hanselmann
2574 27e46076 Michael Hanselmann
2575 24d70417 Michael Hanselmann
def _VerifyCertificateInner(expired, not_before, not_after, now,
2576 24d70417 Michael Hanselmann
                            warn_days, error_days):
2577 24d70417 Michael Hanselmann
  """Verifies certificate validity.
2578 24d70417 Michael Hanselmann

2579 24d70417 Michael Hanselmann
  @type expired: bool
2580 24d70417 Michael Hanselmann
  @param expired: Whether pyOpenSSL considers the certificate as expired
2581 24d70417 Michael Hanselmann
  @type not_before: number or None
2582 24d70417 Michael Hanselmann
  @param not_before: Unix timestamp before which certificate is not valid
2583 24d70417 Michael Hanselmann
  @type not_after: number or None
2584 24d70417 Michael Hanselmann
  @param not_after: Unix timestamp after which certificate is invalid
2585 24d70417 Michael Hanselmann
  @type now: number
2586 24d70417 Michael Hanselmann
  @param now: Current time as Unix timestamp
2587 24d70417 Michael Hanselmann
  @type warn_days: number or None
2588 24d70417 Michael Hanselmann
  @param warn_days: How many days before expiration a warning should be reported
2589 24d70417 Michael Hanselmann
  @type error_days: number or None
2590 24d70417 Michael Hanselmann
  @param error_days: How many days before expiration an error should be reported
2591 24d70417 Michael Hanselmann

2592 24d70417 Michael Hanselmann
  """
2593 24d70417 Michael Hanselmann
  if expired:
2594 24d70417 Michael Hanselmann
    msg = "Certificate is expired"
2595 24d70417 Michael Hanselmann
2596 24d70417 Michael Hanselmann
    if not_before is not None and not_after is not None:
2597 24d70417 Michael Hanselmann
      msg += (" (valid from %s to %s)" %
2598 24d70417 Michael Hanselmann
              (FormatTimestampWithTZ(not_before),
2599 24d70417 Michael Hanselmann
               FormatTimestampWithTZ(not_after)))
2600 24d70417 Michael Hanselmann
    elif not_before is not None:
2601 24d70417 Michael Hanselmann
      msg += " (valid from %s)" % FormatTimestampWithTZ(not_before)
2602 24d70417 Michael Hanselmann
    elif not_after is not None:
2603 24d70417 Michael Hanselmann
      msg += " (valid until %s)" % FormatTimestampWithTZ(not_after)
2604 24d70417 Michael Hanselmann
2605 24d70417 Michael Hanselmann
    return (CERT_ERROR, msg)
2606 24d70417 Michael Hanselmann
2607 24d70417 Michael Hanselmann
  elif not_before is not None and not_before > now:
2608 24d70417 Michael Hanselmann
    return (CERT_WARNING,
2609 24d70417 Michael Hanselmann
            "Certificate not yet valid (valid from %s)" %
2610 24d70417 Michael Hanselmann
            FormatTimestampWithTZ(not_before))
2611 24d70417 Michael Hanselmann
2612 24d70417 Michael Hanselmann
  elif not_after is not None:
2613 24d70417 Michael Hanselmann
    remaining_days = int((not_after - now) / (24 * 3600))
2614 24d70417 Michael Hanselmann
2615 24d70417 Michael Hanselmann
    msg = "Certificate expires in about %d days" % remaining_days
2616 24d70417 Michael Hanselmann
2617 24d70417 Michael Hanselmann
    if error_days is not None and remaining_days <= error_days:
2618 24d70417 Michael Hanselmann
      return (CERT_ERROR, msg)
2619 24d70417 Michael Hanselmann
2620 24d70417 Michael Hanselmann
    if warn_days is not None and remaining_days <= warn_days:
2621 24d70417 Michael Hanselmann
      return (CERT_WARNING, msg)
2622 24d70417 Michael Hanselmann
2623 24d70417 Michael Hanselmann
  return (None, None)
2624 24d70417 Michael Hanselmann
2625 24d70417 Michael Hanselmann
2626 24d70417 Michael Hanselmann
def VerifyX509Certificate(cert, warn_days, error_days):
2627 24d70417 Michael Hanselmann
  """Verifies a certificate for LUVerifyCluster.
2628 24d70417 Michael Hanselmann

2629 24d70417 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2630 24d70417 Michael Hanselmann
  @param cert: X509 certificate object
2631 24d70417 Michael Hanselmann
  @type warn_days: number or None
2632 24d70417 Michael Hanselmann
  @param warn_days: How many days before expiration a warning should be reported
2633 24d70417 Michael Hanselmann
  @type error_days: number or None
2634 24d70417 Michael Hanselmann
  @param error_days: How many days before expiration an error should be reported
2635 24d70417 Michael Hanselmann

2636 24d70417 Michael Hanselmann
  """
2637 24d70417 Michael Hanselmann
  # Depending on the pyOpenSSL version, this can just return (None, None)
2638 24d70417 Michael Hanselmann
  (not_before, not_after) = GetX509CertValidity(cert)
2639 24d70417 Michael Hanselmann
2640 24d70417 Michael Hanselmann
  return _VerifyCertificateInner(cert.has_expired(), not_before, not_after,
2641 24d70417 Michael Hanselmann
                                 time.time(), warn_days, error_days)
2642 24d70417 Michael Hanselmann
2643 24d70417 Michael Hanselmann
2644 68857643 Michael Hanselmann
def SignX509Certificate(cert, key, salt):
2645 68857643 Michael Hanselmann
  """Sign a X509 certificate.
2646 68857643 Michael Hanselmann

2647 68857643 Michael Hanselmann
  An RFC822-like signature header is added in front of the certificate.
2648 68857643 Michael Hanselmann

2649 68857643 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2650 68857643 Michael Hanselmann
  @param cert: X509 certificate object
2651 68857643 Michael Hanselmann
  @type key: string
2652 68857643 Michael Hanselmann
  @param key: Key for HMAC
2653 68857643 Michael Hanselmann
  @type salt: string
2654 68857643 Michael Hanselmann
  @param salt: Salt for HMAC
2655 68857643 Michael Hanselmann
  @rtype: string
2656 68857643 Michael Hanselmann
  @return: Serialized and signed certificate in PEM format
2657 68857643 Michael Hanselmann

2658 68857643 Michael Hanselmann
  """
2659 68857643 Michael Hanselmann
  if not VALID_X509_SIGNATURE_SALT.match(salt):
2660 68857643 Michael Hanselmann
    raise errors.GenericError("Invalid salt: %r" % salt)
2661 68857643 Michael Hanselmann
2662 68857643 Michael Hanselmann
  # Dumping as PEM here ensures the certificate is in a sane format
2663 68857643 Michael Hanselmann
  cert_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
2664 68857643 Michael Hanselmann
2665 68857643 Michael Hanselmann
  return ("%s: %s/%s\n\n%s" %
2666 68857643 Michael Hanselmann
          (constants.X509_CERT_SIGNATURE_HEADER, salt,
2667 3718bf6d Michael Hanselmann
           Sha1Hmac(key, cert_pem, salt=salt),
2668 68857643 Michael Hanselmann
           cert_pem))
2669 68857643 Michael Hanselmann
2670 68857643 Michael Hanselmann
2671 68857643 Michael Hanselmann
def _ExtractX509CertificateSignature(cert_pem):
2672 68857643 Michael Hanselmann
  """Helper function to extract signature from X509 certificate.
2673 68857643 Michael Hanselmann

2674 68857643 Michael Hanselmann
  """
2675 68857643 Michael Hanselmann
  # Extract signature from original PEM data
2676 68857643 Michael Hanselmann
  for line in cert_pem.splitlines():
2677 68857643 Michael Hanselmann
    if line.startswith("---"):
2678 68857643 Michael Hanselmann
      break
2679 68857643 Michael Hanselmann
2680 68857643 Michael Hanselmann
    m = X509_SIGNATURE.match(line.strip())
2681 68857643 Michael Hanselmann
    if m:
2682 68857643 Michael Hanselmann
      return (m.group("salt"), m.group("sign"))
2683 68857643 Michael Hanselmann
2684 68857643 Michael Hanselmann
  raise errors.GenericError("X509 certificate signature is missing")
2685 68857643 Michael Hanselmann
2686 68857643 Michael Hanselmann
2687 68857643 Michael Hanselmann
def LoadSignedX509Certificate(cert_pem, key):
2688 68857643 Michael Hanselmann
  """Verifies a signed X509 certificate.
2689 68857643 Michael Hanselmann

2690 68857643 Michael Hanselmann
  @type cert_pem: string
2691 68857643 Michael Hanselmann
  @param cert_pem: Certificate in PEM format and with signature header
2692 68857643 Michael Hanselmann
  @type key: string
2693 68857643 Michael Hanselmann
  @param key: Key for HMAC
2694 68857643 Michael Hanselmann
  @rtype: tuple; (OpenSSL.crypto.X509, string)
2695 68857643 Michael Hanselmann
  @return: X509 certificate object and salt
2696 68857643 Michael Hanselmann

2697 68857643 Michael Hanselmann
  """
2698 68857643 Michael Hanselmann
  (salt, signature) = _ExtractX509CertificateSignature(cert_pem)
2699 68857643 Michael Hanselmann
2700 68857643 Michael Hanselmann
  # Load certificate
2701 68857643 Michael Hanselmann
  cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_pem)
2702 68857643 Michael Hanselmann
2703 68857643 Michael Hanselmann
  # Dump again to ensure it's in a sane format
2704 68857643 Michael Hanselmann
  sane_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
2705 68857643 Michael Hanselmann
2706 3718bf6d Michael Hanselmann
  if not VerifySha1Hmac(key, sane_pem, signature, salt=salt):
2707 68857643 Michael Hanselmann
    raise errors.GenericError("X509 certificate signature is invalid")
2708 68857643 Michael Hanselmann
2709 68857643 Michael Hanselmann
  return (cert, salt)
2710 68857643 Michael Hanselmann
2711 68857643 Michael Hanselmann
2712 3718bf6d Michael Hanselmann
def Sha1Hmac(key, text, salt=None):
2713 615aaaba Michael Hanselmann
  """Calculates the HMAC-SHA1 digest of a text.
2714 615aaaba Michael Hanselmann

2715 615aaaba Michael Hanselmann
  HMAC is defined in RFC2104.
2716 615aaaba Michael Hanselmann

2717 615aaaba Michael Hanselmann
  @type key: string
2718 615aaaba Michael Hanselmann
  @param key: Secret key
2719 615aaaba Michael Hanselmann
  @type text: string
2720 615aaaba Michael Hanselmann

2721 615aaaba Michael Hanselmann
  """
2722 3718bf6d Michael Hanselmann
  if salt:
2723 3718bf6d Michael Hanselmann
    salted_text = salt + text
2724 3718bf6d Michael Hanselmann
  else:
2725 3718bf6d Michael Hanselmann
    salted_text = text
2726 3718bf6d Michael Hanselmann
2727 716a32cb Guido Trotter
  return hmac.new(key, salted_text, compat.sha1).hexdigest()
2728 615aaaba Michael Hanselmann
2729 615aaaba Michael Hanselmann
2730 3718bf6d Michael Hanselmann
def VerifySha1Hmac(key, text, digest, salt=None):
2731 615aaaba Michael Hanselmann
  """Verifies the HMAC-SHA1 digest of a text.
2732 615aaaba Michael Hanselmann

2733 615aaaba Michael Hanselmann
  HMAC is defined in RFC2104.
2734 615aaaba Michael Hanselmann

2735 615aaaba Michael Hanselmann
  @type key: string
2736 615aaaba Michael Hanselmann
  @param key: Secret key
2737 615aaaba Michael Hanselmann
  @type text: string
2738 615aaaba Michael Hanselmann
  @type digest: string
2739 615aaaba Michael Hanselmann
  @param digest: Expected digest
2740 615aaaba Michael Hanselmann
  @rtype: bool
2741 615aaaba Michael Hanselmann
  @return: Whether HMAC-SHA1 digest matches
2742 615aaaba Michael Hanselmann

2743 615aaaba Michael Hanselmann
  """
2744 3718bf6d Michael Hanselmann
  return digest.lower() == Sha1Hmac(key, text, salt=salt).lower()
2745 615aaaba Michael Hanselmann
2746 615aaaba Michael Hanselmann
2747 26f15862 Iustin Pop
def SafeEncode(text):
2748 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
2749 26f15862 Iustin Pop

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

2759 26f15862 Iustin Pop
  @type text: str or unicode
2760 26f15862 Iustin Pop
  @param text: input data
2761 26f15862 Iustin Pop
  @rtype: str
2762 26f15862 Iustin Pop
  @return: a safe version of text
2763 26f15862 Iustin Pop

2764 26f15862 Iustin Pop
  """
2765 d392fa34 Iustin Pop
  if isinstance(text, unicode):
2766 5bbd3f7f Michael Hanselmann
    # only if unicode; if str already, we handle it below
2767 d392fa34 Iustin Pop
    text = text.encode('ascii', 'backslashreplace')
2768 d392fa34 Iustin Pop
  resu = ""
2769 d392fa34 Iustin Pop
  for char in text:
2770 d392fa34 Iustin Pop
    c = ord(char)
2771 d392fa34 Iustin Pop
    if char  == '\t':
2772 d392fa34 Iustin Pop
      resu += r'\t'
2773 d392fa34 Iustin Pop
    elif char == '\n':
2774 d392fa34 Iustin Pop
      resu += r'\n'
2775 d392fa34 Iustin Pop
    elif char == '\r':
2776 d392fa34 Iustin Pop
      resu += r'\'r'
2777 d392fa34 Iustin Pop
    elif c < 32 or c >= 127: # non-printable
2778 d392fa34 Iustin Pop
      resu += "\\x%02x" % (c & 0xff)
2779 d392fa34 Iustin Pop
    else:
2780 d392fa34 Iustin Pop
      resu += char
2781 d392fa34 Iustin Pop
  return resu
2782 26f15862 Iustin Pop
2783 26f15862 Iustin Pop
2784 5b69bc7c Iustin Pop
def UnescapeAndSplit(text, sep=","):
2785 5b69bc7c Iustin Pop
  """Split and unescape a string based on a given separator.
2786 5b69bc7c Iustin Pop

2787 5b69bc7c Iustin Pop
  This function splits a string based on a separator where the
2788 5b69bc7c Iustin Pop
  separator itself can be escape in order to be an element of the
2789 5b69bc7c Iustin Pop
  elements. The escaping rules are (assuming coma being the
2790 5b69bc7c Iustin Pop
  separator):
2791 5b69bc7c Iustin Pop
    - a plain , separates the elements
2792 5b69bc7c Iustin Pop
    - a sequence \\\\, (double backslash plus comma) is handled as a
2793 5b69bc7c Iustin Pop
      backslash plus a separator comma
2794 5b69bc7c Iustin Pop
    - a sequence \, (backslash plus comma) is handled as a
2795 5b69bc7c Iustin Pop
      non-separator comma
2796 5b69bc7c Iustin Pop

2797 5b69bc7c Iustin Pop
  @type text: string
2798 5b69bc7c Iustin Pop
  @param text: the string to split
2799 5b69bc7c Iustin Pop
  @type sep: string
2800 5b69bc7c Iustin Pop
  @param text: the separator
2801 5b69bc7c Iustin Pop
  @rtype: string
2802 5b69bc7c Iustin Pop
  @return: a list of strings
2803 5b69bc7c Iustin Pop

2804 5b69bc7c Iustin Pop
  """
2805 5b69bc7c Iustin Pop
  # we split the list by sep (with no escaping at this stage)
2806 5b69bc7c Iustin Pop
  slist = text.split(sep)
2807 5b69bc7c Iustin Pop
  # next, we revisit the elements and if any of them ended with an odd
2808 5b69bc7c Iustin Pop
  # number of backslashes, then we join it with the next
2809 5b69bc7c Iustin Pop
  rlist = []
2810 5b69bc7c Iustin Pop
  while slist:
2811 5b69bc7c Iustin Pop
    e1 = slist.pop(0)
2812 5b69bc7c Iustin Pop
    if e1.endswith("\\"):
2813 5b69bc7c Iustin Pop
      num_b = len(e1) - len(e1.rstrip("\\"))
2814 5b69bc7c Iustin Pop
      if num_b % 2 == 1:
2815 5b69bc7c Iustin Pop
        e2 = slist.pop(0)
2816 5b69bc7c Iustin Pop
        # here the backslashes remain (all), and will be reduced in
2817 5b69bc7c Iustin Pop
        # the next step
2818 5b69bc7c Iustin Pop
        rlist.append(e1 + sep + e2)
2819 5b69bc7c Iustin Pop
        continue
2820 5b69bc7c Iustin Pop
    rlist.append(e1)
2821 5b69bc7c Iustin Pop
  # finally, replace backslash-something with something
2822 5b69bc7c Iustin Pop
  rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist]
2823 5b69bc7c Iustin Pop
  return rlist
2824 5b69bc7c Iustin Pop
2825 5b69bc7c Iustin Pop
2826 ab3e6da8 Iustin Pop
def CommaJoin(names):
2827 ab3e6da8 Iustin Pop
  """Nicely join a set of identifiers.
2828 ab3e6da8 Iustin Pop

2829 ab3e6da8 Iustin Pop
  @param names: set, list or tuple
2830 ab3e6da8 Iustin Pop
  @return: a string with the formatted results
2831 ab3e6da8 Iustin Pop

2832 ab3e6da8 Iustin Pop
  """
2833 1f864b60 Iustin Pop
  return ", ".join([str(val) for val in names])
2834 ab3e6da8 Iustin Pop
2835 ab3e6da8 Iustin Pop
2836 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
2837 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
2838 3f6a47a8 Michael Hanselmann

2839 3f6a47a8 Michael Hanselmann
  @type value: int
2840 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
2841 3f6a47a8 Michael Hanselmann
  @rtype: int
2842 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
2843 3f6a47a8 Michael Hanselmann

2844 3f6a47a8 Michael Hanselmann
  """
2845 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
2846 3f6a47a8 Michael Hanselmann
2847 3f6a47a8 Michael Hanselmann
2848 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
2849 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
2850 3f6a47a8 Michael Hanselmann

2851 3f6a47a8 Michael Hanselmann
  @type path: string
2852 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
2853 3f6a47a8 Michael Hanselmann
  @rtype: int
2854 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
2855 3f6a47a8 Michael Hanselmann

2856 3f6a47a8 Michael Hanselmann
  """
2857 3f6a47a8 Michael Hanselmann
  size = 0
2858 3f6a47a8 Michael Hanselmann
2859 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
2860 2a887df9 Michael Hanselmann
    for filename in files:
2861 c4feafe8 Iustin Pop
      st = os.lstat(PathJoin(curpath, filename))
2862 3f6a47a8 Michael Hanselmann
      size += st.st_size
2863 3f6a47a8 Michael Hanselmann
2864 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
2865 3f6a47a8 Michael Hanselmann
2866 3f6a47a8 Michael Hanselmann
2867 1b045f5d Balazs Lecz
def GetMounts(filename=constants.PROC_MOUNTS):
2868 1b045f5d Balazs Lecz
  """Returns the list of mounted filesystems.
2869 1b045f5d Balazs Lecz

2870 1b045f5d Balazs Lecz
  This function is Linux-specific.
2871 1b045f5d Balazs Lecz

2872 1b045f5d Balazs Lecz
  @param filename: path of mounts file (/proc/mounts by default)
2873 1b045f5d Balazs Lecz
  @rtype: list of tuples
2874 1b045f5d Balazs Lecz
  @return: list of mount entries (device, mountpoint, fstype, options)
2875 1b045f5d Balazs Lecz

2876 1b045f5d Balazs Lecz
  """
2877 1b045f5d Balazs Lecz
  # TODO(iustin): investigate non-Linux options (e.g. via mount output)
2878 1b045f5d Balazs Lecz
  data = []
2879 1b045f5d Balazs Lecz
  mountlines = ReadFile(filename).splitlines()
2880 1b045f5d Balazs Lecz
  for line in mountlines:
2881 1b045f5d Balazs Lecz
    device, mountpoint, fstype, options, _ = line.split(None, 4)
2882 1b045f5d Balazs Lecz
    data.append((device, mountpoint, fstype, options))
2883 1b045f5d Balazs Lecz
2884 1b045f5d Balazs Lecz
  return data
2885 1b045f5d Balazs Lecz
2886 1b045f5d Balazs Lecz
2887 620a85fd Iustin Pop
def GetFilesystemStats(path):
2888 620a85fd Iustin Pop
  """Returns the total and free space on a filesystem.
2889 3f6a47a8 Michael Hanselmann

2890 3f6a47a8 Michael Hanselmann
  @type path: string
2891 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
2892 3f6a47a8 Michael Hanselmann
  @rtype: int
2893 620a85fd Iustin Pop
  @return: tuple of (Total space, Free space) in mebibytes
2894 3f6a47a8 Michael Hanselmann

2895 3f6a47a8 Michael Hanselmann
  """
2896 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
2897 3f6a47a8 Michael Hanselmann
2898 620a85fd Iustin Pop
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
2899 620a85fd Iustin Pop
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
2900 620a85fd Iustin Pop
  return (tsize, fsize)
2901 3f6a47a8 Michael Hanselmann
2902 3f6a47a8 Michael Hanselmann
2903 bdefe5dd Michael Hanselmann
def RunInSeparateProcess(fn, *args):
2904 eb58f7bd Michael Hanselmann
  """Runs a function in a separate process.
2905 eb58f7bd Michael Hanselmann

2906 eb58f7bd Michael Hanselmann
  Note: Only boolean return values are supported.
2907 eb58f7bd Michael Hanselmann

2908 eb58f7bd Michael Hanselmann
  @type fn: callable
2909 eb58f7bd Michael Hanselmann
  @param fn: Function to be called
2910 bdefe5dd Michael Hanselmann
  @rtype: bool
2911 bdefe5dd Michael Hanselmann
  @return: Function's result
2912 eb58f7bd Michael Hanselmann

2913 eb58f7bd Michael Hanselmann
  """
2914 eb58f7bd Michael Hanselmann
  pid = os.fork()
2915 eb58f7bd Michael Hanselmann
  if pid == 0:
2916 eb58f7bd Michael Hanselmann
    # Child process
2917 eb58f7bd Michael Hanselmann
    try:
2918 82869978 Michael Hanselmann
      # In case the function uses temporary files
2919 82869978 Michael Hanselmann
      ResetTempfileModule()
2920 82869978 Michael Hanselmann
2921 eb58f7bd Michael Hanselmann
      # Call function
2922 bdefe5dd Michael Hanselmann
      result = int(bool(fn(*args)))
2923 eb58f7bd Michael Hanselmann
      assert result in (0, 1)
2924 eb58f7bd Michael Hanselmann
    except: # pylint: disable-msg=W0702
2925 eb58f7bd Michael Hanselmann
      logging.exception("Error while calling function in separate process")
2926 eb58f7bd Michael Hanselmann
      # 0 and 1 are reserved for the return value
2927 eb58f7bd Michael Hanselmann
      result = 33
2928 eb58f7bd Michael Hanselmann
2929 eb58f7bd Michael Hanselmann
    os._exit(result) # pylint: disable-msg=W0212
2930 eb58f7bd Michael Hanselmann
2931 eb58f7bd Michael Hanselmann
  # Parent process
2932 eb58f7bd Michael Hanselmann
2933 eb58f7bd Michael Hanselmann
  # Avoid zombies and check exit code
2934 eb58f7bd Michael Hanselmann
  (_, status) = os.waitpid(pid, 0)
2935 eb58f7bd Michael Hanselmann
2936 eb58f7bd Michael Hanselmann
  if os.WIFSIGNALED(status):
2937 eb58f7bd Michael Hanselmann
    exitcode = None
2938 eb58f7bd Michael Hanselmann
    signum = os.WTERMSIG(status)
2939 eb58f7bd Michael Hanselmann
  else:
2940 eb58f7bd Michael Hanselmann
    exitcode = os.WEXITSTATUS(status)
2941 eb58f7bd Michael Hanselmann
    signum = None
2942 eb58f7bd Michael Hanselmann
2943 eb58f7bd Michael Hanselmann
  if not (exitcode in (0, 1) and signum is None):
2944 eb58f7bd Michael Hanselmann
    raise errors.GenericError("Child program failed (code=%s, signal=%s)" %
2945 eb58f7bd Michael Hanselmann
                              (exitcode, signum))
2946 eb58f7bd Michael Hanselmann
2947 eb58f7bd Michael Hanselmann
  return bool(exitcode)
2948 eb58f7bd Michael Hanselmann
2949 eb58f7bd Michael Hanselmann
2950 560cbec1 Michael Hanselmann
def IgnoreProcessNotFound(fn, *args, **kwargs):
2951 560cbec1 Michael Hanselmann
  """Ignores ESRCH when calling a process-related function.
2952 560cbec1 Michael Hanselmann

2953 560cbec1 Michael Hanselmann
  ESRCH is raised when a process is not found.
2954 560cbec1 Michael Hanselmann

2955 560cbec1 Michael Hanselmann
  @rtype: bool
2956 560cbec1 Michael Hanselmann
  @return: Whether process was found
2957 560cbec1 Michael Hanselmann

2958 560cbec1 Michael Hanselmann
  """
2959 560cbec1 Michael Hanselmann
  try:
2960 560cbec1 Michael Hanselmann
    fn(*args, **kwargs)
2961 560cbec1 Michael Hanselmann
  except EnvironmentError, err:
2962 560cbec1 Michael Hanselmann
    # Ignore ESRCH
2963 560cbec1 Michael Hanselmann
    if err.errno == errno.ESRCH:
2964 560cbec1 Michael Hanselmann
      return False
2965 560cbec1 Michael Hanselmann
    raise
2966 560cbec1 Michael Hanselmann
2967 560cbec1 Michael Hanselmann
  return True
2968 560cbec1 Michael Hanselmann
2969 560cbec1 Michael Hanselmann
2970 232144d0 Guido Trotter
def IgnoreSignals(fn, *args, **kwargs):
2971 232144d0 Guido Trotter
  """Tries to call a function ignoring failures due to EINTR.
2972 232144d0 Guido Trotter

2973 232144d0 Guido Trotter
  """
2974 232144d0 Guido Trotter
  try:
2975 232144d0 Guido Trotter
    return fn(*args, **kwargs)
2976 965d0e5b Guido Trotter
  except EnvironmentError, err:
2977 2fd7f564 Guido Trotter
    if err.errno == errno.EINTR:
2978 2fd7f564 Guido Trotter
      return None
2979 2fd7f564 Guido Trotter
    else:
2980 232144d0 Guido Trotter
      raise
2981 965d0e5b Guido Trotter
  except (select.error, socket.error), err:
2982 965d0e5b Guido Trotter
    # In python 2.6 and above select.error is an IOError, so it's handled
2983 965d0e5b Guido Trotter
    # above, in 2.5 and below it's not, and it's handled here.
2984 2fd7f564 Guido Trotter
    if err.args and err.args[0] == errno.EINTR:
2985 2fd7f564 Guido Trotter
      return None
2986 2fd7f564 Guido Trotter
    else:
2987 232144d0 Guido Trotter
      raise
2988 232144d0 Guido Trotter
2989 232144d0 Guido Trotter
2990 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
2991 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
2992 eb0f0ce0 Michael Hanselmann

2993 58885d79 Iustin Pop
  @type fd: int
2994 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
2995 58885d79 Iustin Pop

2996 eb0f0ce0 Michael Hanselmann
  """
2997 eb0f0ce0 Michael Hanselmann
  try:
2998 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
2999 eb0f0ce0 Michael Hanselmann
  except IOError, err:
3000 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
3001 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
3002 eb0f0ce0 Michael Hanselmann
    raise
3003 de499029 Michael Hanselmann
3004 de499029 Michael Hanselmann
3005 3b813dd2 Iustin Pop
def FormatTime(val):
3006 3b813dd2 Iustin Pop
  """Formats a time value.
3007 3b813dd2 Iustin Pop

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

3012 3b813dd2 Iustin Pop
  """
3013 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
3014 3b813dd2 Iustin Pop
    return "N/A"
3015 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
3016 3b813dd2 Iustin Pop
  # platforms
3017 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
3018 3b813dd2 Iustin Pop
3019 3b813dd2 Iustin Pop
3020 f8ea4ada Michael Hanselmann
def FormatSeconds(secs):
3021 f8ea4ada Michael Hanselmann
  """Formats seconds for easier reading.
3022 f8ea4ada Michael Hanselmann

3023 f8ea4ada Michael Hanselmann
  @type secs: number
3024 f8ea4ada Michael Hanselmann
  @param secs: Number of seconds
3025 f8ea4ada Michael Hanselmann
  @rtype: string
3026 f8ea4ada Michael Hanselmann
  @return: Formatted seconds (e.g. "2d 9h 19m 49s")
3027 f8ea4ada Michael Hanselmann

3028 f8ea4ada Michael Hanselmann
  """
3029 f8ea4ada Michael Hanselmann
  parts = []
3030 f8ea4ada Michael Hanselmann
3031 f8ea4ada Michael Hanselmann
  secs = round(secs, 0)
3032 f8ea4ada Michael Hanselmann
3033 f8ea4ada Michael Hanselmann
  if secs > 0:
3034 f8ea4ada Michael Hanselmann
    # Negative values would be a bit tricky
3035 f8ea4ada Michael Hanselmann
    for unit, one in [("d", 24 * 60 * 60), ("h", 60 * 60), ("m", 60)]:
3036 f8ea4ada Michael Hanselmann
      (complete, secs) = divmod(secs, one)
3037 f8ea4ada Michael Hanselmann
      if complete or parts:
3038 f8ea4ada Michael Hanselmann
        parts.append("%d%s" % (complete, unit))
3039 f8ea4ada Michael Hanselmann
3040 f8ea4ada Michael Hanselmann
  parts.append("%ds" % secs)
3041 f8ea4ada Michael Hanselmann
3042 f8ea4ada Michael Hanselmann
  return " ".join(parts)
3043 f8ea4ada Michael Hanselmann
3044 f8ea4ada Michael Hanselmann
3045 5cbe43a5 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
3046 05e50653 Michael Hanselmann
  """Reads the watcher pause file.
3047 05e50653 Michael Hanselmann

3048 5cbe43a5 Michael Hanselmann
  @type filename: string
3049 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
3050 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
3051 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
3052 5cbe43a5 Michael Hanselmann
  @type remove_after: int
3053 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
3054 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
3055 5cbe43a5 Michael Hanselmann

3056 05e50653 Michael Hanselmann
  """
3057 05e50653 Michael Hanselmann
  if now is None:
3058 05e50653 Michael Hanselmann
    now = time.time()
3059 05e50653 Michael Hanselmann
3060 05e50653 Michael Hanselmann
  try:
3061 05e50653 Michael Hanselmann
    value = ReadFile(filename)
3062 05e50653 Michael Hanselmann
  except IOError, err:
3063 05e50653 Michael Hanselmann
    if err.errno != errno.ENOENT:
3064 05e50653 Michael Hanselmann
      raise
3065 05e50653 Michael Hanselmann
    value = None
3066 05e50653 Michael Hanselmann
3067 05e50653 Michael Hanselmann
  if value is not None:
3068 05e50653 Michael Hanselmann
    try:
3069 05e50653 Michael Hanselmann
      value = int(value)
3070 05e50653 Michael Hanselmann
    except ValueError:
3071 5cbe43a5 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
3072 5cbe43a5 Michael Hanselmann
                       " removing it"), filename)
3073 5cbe43a5 Michael Hanselmann
      RemoveFile(filename)
3074 05e50653 Michael Hanselmann
      value = None
3075 05e50653 Michael Hanselmann
3076 05e50653 Michael Hanselmann
    if value is not None:
3077 5cbe43a5 Michael Hanselmann
      # Remove file if it's outdated
3078 5cbe43a5 Michael Hanselmann
      if now > (value + remove_after):
3079 5cbe43a5 Michael Hanselmann
        RemoveFile(filename)
3080 5cbe43a5 Michael Hanselmann
        value = None
3081 5cbe43a5 Michael Hanselmann
3082 5cbe43a5 Michael Hanselmann
      elif now > value:
3083 05e50653 Michael Hanselmann
        value = None
3084 05e50653 Michael Hanselmann
3085 05e50653 Michael Hanselmann
  return value
3086 05e50653 Michael Hanselmann
3087 05e50653 Michael Hanselmann
3088 de0ea66b Michael Hanselmann
class RetryTimeout(Exception):
3089 de0ea66b Michael Hanselmann
  """Retry loop timed out.
3090 de0ea66b Michael Hanselmann

3091 40bc67e1 Guido Trotter
  Any arguments which was passed by the retried function to RetryAgain will be
3092 40bc67e1 Guido Trotter
  preserved in RetryTimeout, if it is raised. If such argument was an exception
3093 40bc67e1 Guido Trotter
  the RaiseInner helper method will reraise it.
3094 40bc67e1 Guido Trotter

3095 de0ea66b Michael Hanselmann
  """
3096 506be7c5 Guido Trotter
  def RaiseInner(self):
3097 506be7c5 Guido Trotter
    if self.args and isinstance(self.args[0], Exception):
3098 506be7c5 Guido Trotter
      raise self.args[0]
3099 506be7c5 Guido Trotter
    else:
3100 506be7c5 Guido Trotter
      raise RetryTimeout(*self.args)
3101 de0ea66b Michael Hanselmann
3102 de0ea66b Michael Hanselmann
3103 de0ea66b Michael Hanselmann
class RetryAgain(Exception):
3104 de0ea66b Michael Hanselmann
  """Retry again.
3105 de0ea66b Michael Hanselmann

3106 40bc67e1 Guido Trotter
  Any arguments passed to RetryAgain will be preserved, if a timeout occurs, as
3107 40bc67e1 Guido Trotter
  arguments to RetryTimeout. If an exception is passed, the RaiseInner() method
3108 40bc67e1 Guido Trotter
  of the RetryTimeout() method can be used to reraise it.
3109 40bc67e1 Guido Trotter

3110 de0ea66b Michael Hanselmann
  """
3111 de0ea66b Michael Hanselmann
3112 de0ea66b Michael Hanselmann
3113 de0ea66b Michael Hanselmann
class _RetryDelayCalculator(object):
3114 de0ea66b Michael Hanselmann
  """Calculator for increasing delays.
3115 de0ea66b Michael Hanselmann

3116 de0ea66b Michael Hanselmann
  """
3117 de0ea66b Michael Hanselmann
  __slots__ = [
3118 de0ea66b Michael Hanselmann
    "_factor",
3119 de0ea66b Michael Hanselmann
    "_limit",
3120 de0ea66b Michael Hanselmann
    "_next",
3121 de0ea66b Michael Hanselmann
    "_start",
3122 de0ea66b Michael Hanselmann
    ]
3123 de0ea66b Michael Hanselmann
3124 de0ea66b Michael Hanselmann
  def __init__(self, start, factor, limit):
3125 de0ea66b Michael Hanselmann
    """Initializes this class.
3126 de0ea66b Michael Hanselmann

3127 de0ea66b Michael Hanselmann
    @type start: float
3128 de0ea66b Michael Hanselmann
    @param start: Initial delay
3129 de0ea66b Michael Hanselmann
    @type factor: float
3130 de0ea66b Michael Hanselmann
    @param factor: Factor for delay increase
3131 de0ea66b Michael Hanselmann
    @type limit: float or None
3132 de0ea66b Michael Hanselmann
    @param limit: Upper limit for delay or None for no limit
3133 de0ea66b Michael Hanselmann

3134 de0ea66b Michael Hanselmann
    """
3135 de0ea66b Michael Hanselmann
    assert start > 0.0
3136 de0ea66b Michael Hanselmann
    assert factor >= 1.0
3137 de0ea66b Michael Hanselmann
    assert limit is None or limit >= 0.0
3138 de0ea66b Michael Hanselmann
3139 de0ea66b Michael Hanselmann
    self._start = start
3140 de0ea66b Michael Hanselmann
    self._factor = factor
3141 de0ea66b Michael Hanselmann
    self._limit = limit
3142 de0ea66b Michael Hanselmann
3143 de0ea66b Michael Hanselmann
    self._next = start
3144 de0ea66b Michael Hanselmann
3145 de0ea66b Michael Hanselmann
  def __call__(self):
3146 de0ea66b Michael Hanselmann
    """Returns current delay and calculates the next one.
3147 de0ea66b Michael Hanselmann

3148 de0ea66b Michael Hanselmann
    """
3149 de0ea66b Michael Hanselmann
    current = self._next
3150 de0ea66b Michael Hanselmann
3151 de0ea66b Michael Hanselmann
    # Update for next run
3152 de0ea66b Michael Hanselmann
    if self._limit is None or self._next < self._limit:
3153 26751075 Michael Hanselmann
      self._next = min(self._limit, self._next * self._factor)
3154 de0ea66b Michael Hanselmann
3155 de0ea66b Michael Hanselmann
    return current
3156 de0ea66b Michael Hanselmann
3157 de0ea66b Michael Hanselmann
3158 de0ea66b Michael Hanselmann
#: Special delay to specify whole remaining timeout
3159 de0ea66b Michael Hanselmann
RETRY_REMAINING_TIME = object()
3160 de0ea66b Michael Hanselmann
3161 de0ea66b Michael Hanselmann
3162 de0ea66b Michael Hanselmann
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep,
3163 de0ea66b Michael Hanselmann
          _time_fn=time.time):
3164 de0ea66b Michael Hanselmann
  """Call a function repeatedly until it succeeds.
3165 de0ea66b Michael Hanselmann

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

3170 de0ea66b Michael Hanselmann
  C{delay} can be one of the following:
3171 de0ea66b Michael Hanselmann
    - callable returning the delay length as a float
3172 de0ea66b Michael Hanselmann
    - Tuple of (start, factor, limit)
3173 de0ea66b Michael Hanselmann
    - L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is
3174 de0ea66b Michael Hanselmann
      useful when overriding L{wait_fn} to wait for an external event)
3175 de0ea66b Michael Hanselmann
    - A static delay as a number (int or float)
3176 de0ea66b Michael Hanselmann

3177 de0ea66b Michael Hanselmann
  @type fn: callable
3178 de0ea66b Michael Hanselmann
  @param fn: Function to be called
3179 de0ea66b Michael Hanselmann
  @param delay: Either a callable (returning the delay), a tuple of (start,
3180 de0ea66b Michael Hanselmann
                factor, limit) (see L{_RetryDelayCalculator}),
3181 de0ea66b Michael Hanselmann
                L{RETRY_REMAINING_TIME} or a number (int or float)
3182 de0ea66b Michael Hanselmann
  @type timeout: float
3183 de0ea66b Michael Hanselmann
  @param timeout: Total timeout
3184 de0ea66b Michael Hanselmann
  @type wait_fn: callable
3185 de0ea66b Michael Hanselmann
  @param wait_fn: Waiting function
3186 de0ea66b Michael Hanselmann
  @return: Return value of function
3187 de0ea66b Michael Hanselmann

3188 de0ea66b Michael Hanselmann
  """
3189 de0ea66b Michael Hanselmann
  assert callable(fn)
3190 de0ea66b Michael Hanselmann
  assert callable(wait_fn)
3191 de0ea66b Michael Hanselmann
  assert callable(_time_fn)
3192 de0ea66b Michael Hanselmann
3193 de0ea66b Michael Hanselmann
  if args is None:
3194 de0ea66b Michael Hanselmann
    args = []
3195 de0ea66b Michael Hanselmann
3196 de0ea66b Michael Hanselmann
  end_time = _time_fn() + timeout
3197 de0ea66b Michael Hanselmann
3198 de0ea66b Michael Hanselmann
  if callable(delay):
3199 de0ea66b Michael Hanselmann
    # External function to calculate delay
3200 de0ea66b Michael Hanselmann
    calc_delay = delay
3201 de0ea66b Michael Hanselmann
3202 de0ea66b Michael Hanselmann
  elif isinstance(delay, (tuple, list)):
3203 de0ea66b Michael Hanselmann
    # Increasing delay with optional upper boundary
3204 de0ea66b Michael Hanselmann
    (start, factor, limit) = delay
3205 de0ea66b Michael Hanselmann
    calc_delay = _RetryDelayCalculator(start, factor, limit)
3206 de0ea66b Michael Hanselmann
3207 de0ea66b Michael Hanselmann
  elif delay is RETRY_REMAINING_TIME:
3208 de0ea66b Michael Hanselmann
    # Always use the remaining time
3209 de0ea66b Michael Hanselmann
    calc_delay = None
3210 de0ea66b Michael Hanselmann
3211 de0ea66b Michael Hanselmann
  else:
3212 de0ea66b Michael Hanselmann
    # Static delay
3213 de0ea66b Michael Hanselmann
    calc_delay = lambda: delay
3214 de0ea66b Michael Hanselmann
3215 de0ea66b Michael Hanselmann
  assert calc_delay is None or callable(calc_delay)
3216 de0ea66b Michael Hanselmann
3217 de0ea66b Michael Hanselmann
  while True:
3218 506be7c5 Guido Trotter
    retry_args = []
3219 de0ea66b Michael Hanselmann
    try:
3220 7260cfbe Iustin Pop
      # pylint: disable-msg=W0142
3221 de0ea66b Michael Hanselmann
      return fn(*args)
3222 506be7c5 Guido Trotter
    except RetryAgain, err:
3223 506be7c5 Guido Trotter
      retry_args = err.args
3224 1b429e2a Iustin Pop
    except RetryTimeout:
3225 1b429e2a Iustin Pop
      raise errors.ProgrammerError("Nested retry loop detected that didn't"
3226 1b429e2a Iustin Pop
                                   " handle RetryTimeout")
3227 de0ea66b Michael Hanselmann
3228 de0ea66b Michael Hanselmann
    remaining_time = end_time - _time_fn()
3229 de0ea66b Michael Hanselmann
3230 de0ea66b Michael Hanselmann
    if remaining_time < 0.0:
3231 506be7c5 Guido Trotter
      # pylint: disable-msg=W0142
3232 506be7c5 Guido Trotter
      raise RetryTimeout(*retry_args)
3233 de0ea66b Michael Hanselmann
3234 de0ea66b Michael Hanselmann
    assert remaining_time >= 0.0
3235 de0ea66b Michael Hanselmann
3236 de0ea66b Michael Hanselmann
    if calc_delay is None:
3237 de0ea66b Michael Hanselmann
      wait_fn(remaining_time)
3238 de0ea66b Michael Hanselmann
    else:
3239 de0ea66b Michael Hanselmann
      current_delay = calc_delay()
3240 de0ea66b Michael Hanselmann
      if current_delay > 0.0:
3241 de0ea66b Michael Hanselmann
        wait_fn(current_delay)
3242 de0ea66b Michael Hanselmann
3243 de0ea66b Michael Hanselmann
3244 bdd5e420 Michael Hanselmann
def GetClosedTempfile(*args, **kwargs):
3245 bdd5e420 Michael Hanselmann
  """Creates a temporary file and returns its path.
3246 a55474c7 Michael Hanselmann

3247 bdd5e420 Michael Hanselmann
  """
3248 bdd5e420 Michael Hanselmann
  (fd, path) = tempfile.mkstemp(*args, **kwargs)
3249 bdd5e420 Michael Hanselmann
  _CloseFDNoErr(fd)
3250 bdd5e420 Michael Hanselmann
  return path
3251 bdd5e420 Michael Hanselmann
3252 bdd5e420 Michael Hanselmann
3253 bdd5e420 Michael Hanselmann
def GenerateSelfSignedX509Cert(common_name, validity):
3254 bdd5e420 Michael Hanselmann
  """Generates a self-signed X509 certificate.
3255 bdd5e420 Michael Hanselmann

3256 bdd5e420 Michael Hanselmann
  @type common_name: string
3257 bdd5e420 Michael Hanselmann
  @param common_name: commonName value
3258 a55474c7 Michael Hanselmann
  @type validity: int
3259 bdd5e420 Michael Hanselmann
  @param validity: Validity for certificate in seconds
3260 a55474c7 Michael Hanselmann

3261 a55474c7 Michael Hanselmann
  """
3262 bdd5e420 Michael Hanselmann
  # Create private and public key
3263 bdd5e420 Michael Hanselmann
  key = OpenSSL.crypto.PKey()
3264 bdd5e420 Michael Hanselmann
  key.generate_key(OpenSSL.crypto.TYPE_RSA, constants.RSA_KEY_BITS)
3265 bdd5e420 Michael Hanselmann
3266 bdd5e420 Michael Hanselmann
  # Create self-signed certificate
3267 bdd5e420 Michael Hanselmann
  cert = OpenSSL.crypto.X509()
3268 bdd5e420 Michael Hanselmann
  if common_name:
3269 bdd5e420 Michael Hanselmann
    cert.get_subject().CN = common_name
3270 bdd5e420 Michael Hanselmann
  cert.set_serial_number(1)
3271 bdd5e420 Michael Hanselmann
  cert.gmtime_adj_notBefore(0)
3272 bdd5e420 Michael Hanselmann
  cert.gmtime_adj_notAfter(validity)
3273 bdd5e420 Michael Hanselmann
  cert.set_issuer(cert.get_subject())
3274 bdd5e420 Michael Hanselmann
  cert.set_pubkey(key)
3275 bdd5e420 Michael Hanselmann
  cert.sign(key, constants.X509_CERT_SIGN_DIGEST)
3276 bdd5e420 Michael Hanselmann
3277 bdd5e420 Michael Hanselmann
  key_pem = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key)
3278 bdd5e420 Michael Hanselmann
  cert_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
3279 bdd5e420 Michael Hanselmann
3280 bdd5e420 Michael Hanselmann
  return (key_pem, cert_pem)
3281 bdd5e420 Michael Hanselmann
3282 bdd5e420 Michael Hanselmann
3283 bdd5e420 Michael Hanselmann
def GenerateSelfSignedSslCert(filename, validity=(5 * 365)):
3284 bdd5e420 Michael Hanselmann
  """Legacy function to generate self-signed X509 certificate.
3285 bdd5e420 Michael Hanselmann

3286 bdd5e420 Michael Hanselmann
  """
3287 bdd5e420 Michael Hanselmann
  (key_pem, cert_pem) = GenerateSelfSignedX509Cert(None,
3288 bdd5e420 Michael Hanselmann
                                                   validity * 24 * 60 * 60)
3289 bdd5e420 Michael Hanselmann
3290 bdd5e420 Michael Hanselmann
  WriteFile(filename, mode=0400, data=key_pem + cert_pem)
3291 a55474c7 Michael Hanselmann
3292 a55474c7 Michael Hanselmann
3293 a87b4824 Michael Hanselmann
class FileLock(object):
3294 a87b4824 Michael Hanselmann
  """Utility class for file locks.
3295 a87b4824 Michael Hanselmann

3296 a87b4824 Michael Hanselmann
  """
3297 b4478d34 Michael Hanselmann
  def __init__(self, fd, filename):
3298 58885d79 Iustin Pop
    """Constructor for FileLock.
3299 58885d79 Iustin Pop

3300 b4478d34 Michael Hanselmann
    @type fd: file
3301 b4478d34 Michael Hanselmann
    @param fd: File object
3302 58885d79 Iustin Pop
    @type filename: str
3303 b4478d34 Michael Hanselmann
    @param filename: Path of the file opened at I{fd}
3304 58885d79 Iustin Pop

3305 58885d79 Iustin Pop
    """
3306 b4478d34 Michael Hanselmann
    self.fd = fd
3307 a87b4824 Michael Hanselmann
    self.filename = filename
3308 b4478d34 Michael Hanselmann
3309 b4478d34 Michael Hanselmann
  @classmethod
3310 b4478d34 Michael Hanselmann
  def Open(cls, filename):
3311 b4478d34 Michael Hanselmann
    """Creates and opens a file to be used as a file-based lock.
3312 b4478d34 Michael Hanselmann

3313 b4478d34 Michael Hanselmann
    @type filename: string
3314 b4478d34 Michael Hanselmann
    @param filename: path to the file to be locked
3315 b4478d34 Michael Hanselmann

3316 b4478d34 Michael Hanselmann
    """
3317 b4478d34 Michael Hanselmann
    # Using "os.open" is necessary to allow both opening existing file
3318 b4478d34 Michael Hanselmann
    # read/write and creating if not existing. Vanilla "open" will truncate an
3319 b4478d34 Michael Hanselmann
    # existing file -or- allow creating if not existing.
3320 b4478d34 Michael Hanselmann
    return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), "w+"),
3321 b4478d34 Michael Hanselmann
               filename)
3322 a87b4824 Michael Hanselmann
3323 a87b4824 Michael Hanselmann
  def __del__(self):
3324 a87b4824 Michael Hanselmann
    self.Close()
3325 a87b4824 Michael Hanselmann
3326 a87b4824 Michael Hanselmann
  def Close(self):
3327 58885d79 Iustin Pop
    """Close the file and release the lock.
3328 58885d79 Iustin Pop

3329 58885d79 Iustin Pop
    """
3330 aac3fbf0 Iustin Pop
    if hasattr(self, "fd") and self.fd:
3331 a87b4824 Michael Hanselmann
      self.fd.close()
3332 a87b4824 Michael Hanselmann
      self.fd = None
3333 a87b4824 Michael Hanselmann
3334 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
3335 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
3336 aa74b828 Michael Hanselmann

3337 aa74b828 Michael Hanselmann
    @type flag: int
3338 58885d79 Iustin Pop
    @param flag: operation flag
3339 aa74b828 Michael Hanselmann
    @type blocking: bool
3340 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
3341 aa74b828 Michael Hanselmann
    @type timeout: None or float
3342 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
3343 aa74b828 Michael Hanselmann
                    non-blocking mode).
3344 aa74b828 Michael Hanselmann
    @type errmsg: string
3345 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
3346 aa74b828 Michael Hanselmann

3347 aa74b828 Michael Hanselmann
    """
3348 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
3349 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
3350 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
3351 cc4c9b91 Michael Hanselmann
    assert not (flag & fcntl.LOCK_NB), "LOCK_NB must not be set"
3352 a87b4824 Michael Hanselmann
3353 cc4c9b91 Michael Hanselmann
    # When a timeout is used, LOCK_NB must always be set
3354 cc4c9b91 Michael Hanselmann
    if not (timeout is None and blocking):
3355 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
3356 a87b4824 Michael Hanselmann
3357 cc4c9b91 Michael Hanselmann
    if timeout is None:
3358 cc4c9b91 Michael Hanselmann
      self._Lock(self.fd, flag, timeout)
3359 cc4c9b91 Michael Hanselmann
    else:
3360 cc4c9b91 Michael Hanselmann
      try:
3361 cc4c9b91 Michael Hanselmann
        Retry(self._Lock, (0.1, 1.2, 1.0), timeout,
3362 cc4c9b91 Michael Hanselmann
              args=(self.fd, flag, timeout))
3363 cc4c9b91 Michael Hanselmann
      except RetryTimeout:
3364 cc4c9b91 Michael Hanselmann
        raise errors.LockError(errmsg)
3365 aa74b828 Michael Hanselmann
3366 cc4c9b91 Michael Hanselmann
  @staticmethod
3367 cc4c9b91 Michael Hanselmann
  def _Lock(fd, flag, timeout):
3368 cc4c9b91 Michael Hanselmann
    try:
3369 cc4c9b91 Michael Hanselmann
      fcntl.flock(fd, flag)
3370 cc4c9b91 Michael Hanselmann
    except IOError, err:
3371 cc4c9b91 Michael Hanselmann
      if timeout is not None and err.errno == errno.EAGAIN:
3372 cc4c9b91 Michael Hanselmann
        raise RetryAgain()
3373 31892b4c Michael Hanselmann
3374 cc4c9b91 Michael Hanselmann
      logging.exception("fcntl.flock failed")
3375 cc4c9b91 Michael Hanselmann
      raise
3376 aa74b828 Michael Hanselmann
3377 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
3378 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
3379 a87b4824 Michael Hanselmann

3380 58885d79 Iustin Pop
    @type blocking: boolean
3381 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
3382 58885d79 Iustin Pop
        can lock the file or return immediately
3383 58885d79 Iustin Pop
    @type timeout: int or None
3384 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
3385 58885d79 Iustin Pop
        (in blocking mode)
3386 58885d79 Iustin Pop

3387 a87b4824 Michael Hanselmann
    """
3388 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
3389 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
3390 a87b4824 Michael Hanselmann
3391 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
3392 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
3393 a87b4824 Michael Hanselmann

3394 58885d79 Iustin Pop
    @type blocking: boolean
3395 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
3396 58885d79 Iustin Pop
        can lock the file or return immediately
3397 58885d79 Iustin Pop
    @type timeout: int or None
3398 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
3399 58885d79 Iustin Pop
        (in blocking mode)
3400 58885d79 Iustin Pop

3401 a87b4824 Michael Hanselmann
    """
3402 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
3403 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
3404 a87b4824 Michael Hanselmann
3405 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
3406 a87b4824 Michael Hanselmann
    """Unlocks the file.
3407 a87b4824 Michael Hanselmann

3408 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
3409 58885d79 Iustin Pop
    operation::
3410 58885d79 Iustin Pop

3411 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
3412 58885d79 Iustin Pop
      operations.
3413 58885d79 Iustin Pop

3414 58885d79 Iustin Pop
    @type blocking: boolean
3415 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
3416 58885d79 Iustin Pop
        can lock the file or return immediately
3417 58885d79 Iustin Pop
    @type timeout: int or None
3418 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
3419 58885d79 Iustin Pop
        (in blocking mode)
3420 a87b4824 Michael Hanselmann

3421 a87b4824 Michael Hanselmann
    """
3422 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
3423 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
3424 a87b4824 Michael Hanselmann
3425 a87b4824 Michael Hanselmann
3426 339be5a8 Michael Hanselmann
class LineSplitter:
3427 339be5a8 Michael Hanselmann
  """Splits data chunks into lines separated by newline.
3428 339be5a8 Michael Hanselmann

3429 339be5a8 Michael Hanselmann
  Instances provide a file-like interface.
3430 339be5a8 Michael Hanselmann

3431 339be5a8 Michael Hanselmann
  """
3432 339be5a8 Michael Hanselmann
  def __init__(self, line_fn, *args):
3433 339be5a8 Michael Hanselmann
    """Initializes this class.
3434 339be5a8 Michael Hanselmann

3435 339be5a8 Michael Hanselmann
    @type line_fn: callable
3436 339be5a8 Michael Hanselmann
    @param line_fn: Function called for each line, first parameter is line
3437 339be5a8 Michael Hanselmann
    @param args: Extra arguments for L{line_fn}
3438 339be5a8 Michael Hanselmann

3439 339be5a8 Michael Hanselmann
    """
3440 339be5a8 Michael Hanselmann
    assert callable(line_fn)
3441 339be5a8 Michael Hanselmann
3442 339be5a8 Michael Hanselmann
    if args:
3443 339be5a8 Michael Hanselmann
      # Python 2.4 doesn't have functools.partial yet
3444 339be5a8 Michael Hanselmann
      self._line_fn = \
3445 339be5a8 Michael Hanselmann
        lambda line: line_fn(line, *args) # pylint: disable-msg=W0142
3446 339be5a8 Michael Hanselmann
    else:
3447 339be5a8 Michael Hanselmann
      self._line_fn = line_fn
3448 339be5a8 Michael Hanselmann
3449 339be5a8 Michael Hanselmann
    self._lines = collections.deque()
3450 339be5a8 Michael Hanselmann
    self._buffer = ""
3451 339be5a8 Michael Hanselmann
3452 339be5a8 Michael Hanselmann
  def write(self, data):
3453 339be5a8 Michael Hanselmann
    parts = (self._buffer + data).split("\n")
3454 339be5a8 Michael Hanselmann
    self._buffer = parts.pop()
3455 339be5a8 Michael Hanselmann
    self._lines.extend(parts)
3456 339be5a8 Michael Hanselmann
3457 339be5a8 Michael Hanselmann
  def flush(self):
3458 339be5a8 Michael Hanselmann
    while self._lines:
3459 339be5a8 Michael Hanselmann
      self._line_fn(self._lines.popleft().rstrip("\r\n"))
3460 339be5a8 Michael Hanselmann
3461 339be5a8 Michael Hanselmann
  def close(self):
3462 339be5a8 Michael Hanselmann
    self.flush()
3463 339be5a8 Michael Hanselmann
    if self._buffer:
3464 339be5a8 Michael Hanselmann
      self._line_fn(self._buffer)
3465 339be5a8 Michael Hanselmann
3466 339be5a8 Michael Hanselmann
3467 451575de Guido Trotter
def SignalHandled(signums):
3468 451575de Guido Trotter
  """Signal Handled decoration.
3469 451575de Guido Trotter

3470 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
3471 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
3472 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
3473 451575de Guido Trotter
  objects as values.
3474 451575de Guido Trotter

3475 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
3476 451575de Guido Trotter
  with different handlers.
3477 451575de Guido Trotter

3478 451575de Guido Trotter
  @type signums: list
3479 451575de Guido Trotter
  @param signums: signals to intercept
3480 451575de Guido Trotter

3481 451575de Guido Trotter
  """
3482 451575de Guido Trotter
  def wrap(fn):
3483 451575de Guido Trotter
    def sig_function(*args, **kwargs):
3484 451575de Guido Trotter
      assert 'signal_handlers' not in kwargs or \
3485 451575de Guido Trotter
             kwargs['signal_handlers'] is None or \
3486 451575de Guido Trotter
             isinstance(kwargs['signal_handlers'], dict), \
3487 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
3488 451575de Guido Trotter
      if 'signal_handlers' in kwargs and kwargs['signal_handlers'] is not None:
3489 451575de Guido Trotter
        signal_handlers = kwargs['signal_handlers']
3490 451575de Guido Trotter
      else:
3491 451575de Guido Trotter
        signal_handlers = {}
3492 451575de Guido Trotter
        kwargs['signal_handlers'] = signal_handlers
3493 451575de Guido Trotter
      sighandler = SignalHandler(signums)
3494 451575de Guido Trotter
      try:
3495 451575de Guido Trotter
        for sig in signums:
3496 451575de Guido Trotter
          signal_handlers[sig] = sighandler
3497 451575de Guido Trotter
        return fn(*args, **kwargs)
3498 451575de Guido Trotter
      finally:
3499 451575de Guido Trotter
        sighandler.Reset()
3500 451575de Guido Trotter
    return sig_function
3501 451575de Guido Trotter
  return wrap
3502 451575de Guido Trotter
3503 451575de Guido Trotter
3504 b9768937 Michael Hanselmann
class SignalWakeupFd(object):
3505 b9768937 Michael Hanselmann
  try:
3506 b9768937 Michael Hanselmann
    # This is only supported in Python 2.5 and above (some distributions
3507 b9768937 Michael Hanselmann
    # backported it to Python 2.4)
3508 b9768937 Michael Hanselmann
    _set_wakeup_fd_fn = signal.set_wakeup_fd
3509 b9768937 Michael Hanselmann
  except AttributeError:
3510 b9768937 Michael Hanselmann
    # Not supported
3511 b9768937 Michael Hanselmann
    def _SetWakeupFd(self, _): # pylint: disable-msg=R0201
3512 b9768937 Michael Hanselmann
      return -1
3513 b9768937 Michael Hanselmann
  else:
3514 b9768937 Michael Hanselmann
    def _SetWakeupFd(self, fd):
3515 b9768937 Michael Hanselmann
      return self._set_wakeup_fd_fn(fd)
3516 b9768937 Michael Hanselmann
3517 b9768937 Michael Hanselmann
  def __init__(self):
3518 b9768937 Michael Hanselmann
    """Initializes this class.
3519 b9768937 Michael Hanselmann

3520 b9768937 Michael Hanselmann
    """
3521 b9768937 Michael Hanselmann
    (read_fd, write_fd) = os.pipe()
3522 b9768937 Michael Hanselmann
3523 b9768937 Michael Hanselmann
    # Once these succeeded, the file descriptors will be closed automatically.
3524 b9768937 Michael Hanselmann
    # Buffer size 0 is important, otherwise .read() with a specified length
3525 b9768937 Michael Hanselmann
    # might buffer data and the file descriptors won't be marked readable.
3526 b9768937 Michael Hanselmann
    self._read_fh = os.fdopen(read_fd, "r", 0)
3527 b9768937 Michael Hanselmann
    self._write_fh = os.fdopen(write_fd, "w", 0)
3528 b9768937 Michael Hanselmann
3529 b9768937 Michael Hanselmann
    self._previous = self._SetWakeupFd(self._write_fh.fileno())
3530 b9768937 Michael Hanselmann
3531 b9768937 Michael Hanselmann
    # Utility functions
3532 b9768937 Michael Hanselmann
    self.fileno = self._read_fh.fileno
3533 b9768937 Michael Hanselmann
    self.read = self._read_fh.read
3534 b9768937 Michael Hanselmann
3535 b9768937 Michael Hanselmann
  def Reset(self):
3536 b9768937 Michael Hanselmann
    """Restores the previous wakeup file descriptor.
3537 b9768937 Michael Hanselmann

3538 b9768937 Michael Hanselmann
    """
3539 b9768937 Michael Hanselmann
    if hasattr(self, "_previous") and self._previous is not None:
3540 b9768937 Michael Hanselmann
      self._SetWakeupFd(self._previous)
3541 b9768937 Michael Hanselmann
      self._previous = None
3542 b9768937 Michael Hanselmann
3543 b9768937 Michael Hanselmann
  def Notify(self):
3544 b9768937 Michael Hanselmann
    """Notifies the wakeup file descriptor.
3545 b9768937 Michael Hanselmann

3546 b9768937 Michael Hanselmann
    """
3547 b9768937 Michael Hanselmann
    self._write_fh.write("\0")
3548 b9768937 Michael Hanselmann
3549 b9768937 Michael Hanselmann
  def __del__(self):
3550 b9768937 Michael Hanselmann
    """Called before object deletion.
3551 b9768937 Michael Hanselmann

3552 b9768937 Michael Hanselmann
    """
3553 b9768937 Michael Hanselmann
    self.Reset()
3554 b9768937 Michael Hanselmann
3555 b9768937 Michael Hanselmann
3556 de499029 Michael Hanselmann
class SignalHandler(object):
3557 de499029 Michael Hanselmann
  """Generic signal handler class.
3558 de499029 Michael Hanselmann

3559 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
3560 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
3561 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
3562 58885d79 Iustin Pop
  signal was sent.
3563 58885d79 Iustin Pop

3564 58885d79 Iustin Pop
  @type signum: list
3565 58885d79 Iustin Pop
  @ivar signum: the signals we handle
3566 58885d79 Iustin Pop
  @type called: boolean
3567 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
3568 de499029 Michael Hanselmann

3569 de499029 Michael Hanselmann
  """
3570 b9768937 Michael Hanselmann
  def __init__(self, signum, handler_fn=None, wakeup=None):
3571 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
3572 de499029 Michael Hanselmann

3573 58885d79 Iustin Pop
    @type signum: int or list of ints
3574 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
3575 92b61ec7 Michael Hanselmann
    @type handler_fn: callable
3576 92b61ec7 Michael Hanselmann
    @param handler_fn: Signal handling function
3577 de499029 Michael Hanselmann

3578 de499029 Michael Hanselmann
    """
3579 92b61ec7 Michael Hanselmann
    assert handler_fn is None or callable(handler_fn)
3580 92b61ec7 Michael Hanselmann
3581 6c52849e Guido Trotter
    self.signum = set(signum)
3582 de499029 Michael Hanselmann
    self.called = False
3583 de499029 Michael Hanselmann
3584 92b61ec7 Michael Hanselmann
    self._handler_fn = handler_fn
3585 b9768937 Michael Hanselmann
    self._wakeup = wakeup
3586 92b61ec7 Michael Hanselmann
3587 de499029 Michael Hanselmann
    self._previous = {}
3588 de499029 Michael Hanselmann
    try:
3589 de499029 Michael Hanselmann
      for signum in self.signum:
3590 de499029 Michael Hanselmann
        # Setup handler
3591 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
3592 de499029 Michael Hanselmann
        try:
3593 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
3594 de499029 Michael Hanselmann
        except:
3595 de499029 Michael Hanselmann
          # Restore previous handler
3596 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
3597 de499029 Michael Hanselmann
          raise
3598 de499029 Michael Hanselmann
    except:
3599 de499029 Michael Hanselmann
      # Reset all handlers
3600 de499029 Michael Hanselmann
      self.Reset()
3601 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
3602 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
3603 de499029 Michael Hanselmann
      raise
3604 de499029 Michael Hanselmann
3605 de499029 Michael Hanselmann
  def __del__(self):
3606 de499029 Michael Hanselmann
    self.Reset()
3607 de499029 Michael Hanselmann
3608 de499029 Michael Hanselmann
  def Reset(self):
3609 de499029 Michael Hanselmann
    """Restore previous handler.
3610 de499029 Michael Hanselmann

3611 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
3612 58885d79 Iustin Pop

3613 de499029 Michael Hanselmann
    """
3614 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
3615 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
3616 de499029 Michael Hanselmann
      # If successful, remove from dict
3617 de499029 Michael Hanselmann
      del self._previous[signum]
3618 de499029 Michael Hanselmann
3619 de499029 Michael Hanselmann
  def Clear(self):
3620 58885d79 Iustin Pop
    """Unsets the L{called} flag.
3621 de499029 Michael Hanselmann

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

3624 de499029 Michael Hanselmann
    """
3625 de499029 Michael Hanselmann
    self.called = False
3626 de499029 Michael Hanselmann
3627 92b61ec7 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
3628 de499029 Michael Hanselmann
    """Actual signal handling function.
3629 de499029 Michael Hanselmann

3630 de499029 Michael Hanselmann
    """
3631 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
3632 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
3633 de499029 Michael Hanselmann
    self.called = True
3634 a2d2e1a7 Iustin Pop
3635 b9768937 Michael Hanselmann
    if self._wakeup:
3636 b9768937 Michael Hanselmann
      # Notify whoever is interested in signals
3637 b9768937 Michael Hanselmann
      self._wakeup.Notify()
3638 b9768937 Michael Hanselmann
3639 92b61ec7 Michael Hanselmann
    if self._handler_fn:
3640 92b61ec7 Michael Hanselmann
      self._handler_fn(signum, frame)
3641 92b61ec7 Michael Hanselmann
3642 a2d2e1a7 Iustin Pop
3643 a2d2e1a7 Iustin Pop
class FieldSet(object):
3644 a2d2e1a7 Iustin Pop
  """A simple field set.
3645 a2d2e1a7 Iustin Pop

3646 a2d2e1a7 Iustin Pop
  Among the features are:
3647 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
3648 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
3649 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
3650 a2d2e1a7 Iustin Pop

3651 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
3652 a2d2e1a7 Iustin Pop

3653 a2d2e1a7 Iustin Pop
  """
3654 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
3655 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
3656 a2d2e1a7 Iustin Pop
3657 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
3658 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
3659 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
3660 a2d2e1a7 Iustin Pop
3661 a2d2e1a7 Iustin Pop
  def Matches(self, field):
3662 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
3663 a2d2e1a7 Iustin Pop

3664 a2d2e1a7 Iustin Pop
    @type field: str
3665 a2d2e1a7 Iustin Pop
    @param field: the string to match
3666 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
3667 a2d2e1a7 Iustin Pop

3668 a2d2e1a7 Iustin Pop
    """
3669 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
3670 a2d2e1a7 Iustin Pop
      return m
3671 6c881c52 Iustin Pop
    return None
3672 a2d2e1a7 Iustin Pop
3673 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
3674 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
3675 a2d2e1a7 Iustin Pop

3676 a2d2e1a7 Iustin Pop
    @type items: list
3677 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
3678 a2d2e1a7 Iustin Pop
    @rtype: list
3679 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
3680 a2d2e1a7 Iustin Pop

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