Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 0070a462

History | View | Annotate | Download (102.3 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 600535f0 Manuel Franceschini
# Copyright (C) 2006, 2007, 2010 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 a8083063 Iustin Pop
65 16abfbc2 Alexander Schreiber
66 a8083063 Iustin Pop
_locksheld = []
67 a8083063 Iustin Pop
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
68 a8083063 Iustin Pop
69 e67bd559 Michael Hanselmann
debug_locks = False
70 58885d79 Iustin Pop
71 58885d79 Iustin Pop
#: when set to True, L{RunCmd} is disabled
72 b74159ee Iustin Pop
no_fork = False
73 f362096f Iustin Pop
74 13998ef2 Michael Hanselmann
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
75 13998ef2 Michael Hanselmann
76 68857643 Michael Hanselmann
HEX_CHAR_RE = r"[a-zA-Z0-9]"
77 68857643 Michael Hanselmann
VALID_X509_SIGNATURE_SALT = re.compile("^%s+$" % HEX_CHAR_RE, re.S)
78 68857643 Michael Hanselmann
X509_SIGNATURE = re.compile(r"^%s:\s*(?P<salt>%s+)/(?P<sign>%s+)$" %
79 68857643 Michael Hanselmann
                            (re.escape(constants.X509_CERT_SIGNATURE_HEADER),
80 68857643 Michael Hanselmann
                             HEX_CHAR_RE, HEX_CHAR_RE),
81 68857643 Michael Hanselmann
                            re.S | re.I)
82 68857643 Michael Hanselmann
83 28f34048 Michael Hanselmann
_VALID_SERVICE_NAME_RE = re.compile("^[-_.a-zA-Z0-9]{1,128}$")
84 28f34048 Michael Hanselmann
85 24d70417 Michael Hanselmann
# Certificate verification results
86 24d70417 Michael Hanselmann
(CERT_WARNING,
87 24d70417 Michael Hanselmann
 CERT_ERROR) = range(1, 3)
88 24d70417 Michael Hanselmann
89 4b6fa0bf Luca Bigliardi
# Flags for mlockall() (from bits/mman.h)
90 4b6fa0bf Luca Bigliardi
_MCL_CURRENT = 1
91 4b6fa0bf Luca Bigliardi
_MCL_FUTURE = 2
92 4b6fa0bf Luca Bigliardi
93 7c0d6283 Michael Hanselmann
94 a8083063 Iustin Pop
class RunResult(object):
95 58885d79 Iustin Pop
  """Holds the result of running external programs.
96 58885d79 Iustin Pop

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

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

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

148 c1dd99d4 Michael Hanselmann
  """
149 bb3776b4 Michael Hanselmann
  if reset:
150 bb3776b4 Michael Hanselmann
    cmd_env = {}
151 bb3776b4 Michael Hanselmann
  else:
152 bb3776b4 Michael Hanselmann
    cmd_env = os.environ.copy()
153 bb3776b4 Michael Hanselmann
    cmd_env["LC_ALL"] = "C"
154 bb3776b4 Michael Hanselmann
155 c1dd99d4 Michael Hanselmann
  if env is not None:
156 c1dd99d4 Michael Hanselmann
    cmd_env.update(env)
157 bb3776b4 Michael Hanselmann
158 c1dd99d4 Michael Hanselmann
  return cmd_env
159 c1dd99d4 Michael Hanselmann
160 c1dd99d4 Michael Hanselmann
161 0963d545 Renรฉ Nussbaumer
def RunCmd(cmd, env=None, output=None, cwd="/", reset_env=False,
162 0963d545 Renรฉ Nussbaumer
           interactive=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 0963d545 Renรฉ Nussbaumer
  @type interactive: boolean
182 0963d545 Renรฉ Nussbaumer
  @param interactive: weather we pipe stdin, stdout and stderr
183 0963d545 Renรฉ Nussbaumer
                      (default behaviour) or run the command interactive
184 36117c2b Iustin Pop
  @rtype: L{RunResult}
185 58885d79 Iustin Pop
  @return: RunResult instance
186 5bbd3f7f Michael Hanselmann
  @raise errors.ProgrammerError: if we call this when forks are disabled
187 a8083063 Iustin Pop

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

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

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

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

432 36117c2b Iustin Pop
  @type  cmd: string or list
433 36117c2b Iustin Pop
  @param cmd: Command to run
434 36117c2b Iustin Pop
  @type env: dict
435 36117c2b Iustin Pop
  @param env: The environment to use
436 36117c2b Iustin Pop
  @type via_shell: bool
437 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
438 8797df43 Iustin Pop
  @type cwd: string
439 8797df43 Iustin Pop
  @param cwd: the working directory for the program
440 0963d545 Renรฉ Nussbaumer
  @type interactive: boolean
441 0963d545 Renรฉ Nussbaumer
  @param interactive: Run command interactive (without piping)
442 36117c2b Iustin Pop
  @rtype: tuple
443 36117c2b Iustin Pop
  @return: (out, err, status)
444 36117c2b Iustin Pop

445 36117c2b Iustin Pop
  """
446 9c233417 Iustin Pop
  poller = select.poll()
447 0963d545 Renรฉ Nussbaumer
448 0963d545 Renรฉ Nussbaumer
  stderr = subprocess.PIPE
449 0963d545 Renรฉ Nussbaumer
  stdout = subprocess.PIPE
450 0963d545 Renรฉ Nussbaumer
  stdin = subprocess.PIPE
451 0963d545 Renรฉ Nussbaumer
452 0963d545 Renรฉ Nussbaumer
  if interactive:
453 0963d545 Renรฉ Nussbaumer
    stderr = stdout = stdin = None
454 0963d545 Renรฉ Nussbaumer
455 36117c2b Iustin Pop
  child = subprocess.Popen(cmd, shell=via_shell,
456 0963d545 Renรฉ Nussbaumer
                           stderr=stderr,
457 0963d545 Renรฉ Nussbaumer
                           stdout=stdout,
458 0963d545 Renรฉ Nussbaumer
                           stdin=stdin,
459 8797df43 Iustin Pop
                           close_fds=True, env=env,
460 8797df43 Iustin Pop
                           cwd=cwd)
461 113b55aa Iustin Pop
462 9c233417 Iustin Pop
  out = StringIO()
463 9c233417 Iustin Pop
  err = StringIO()
464 0963d545 Renรฉ Nussbaumer
  if not interactive:
465 0963d545 Renรฉ Nussbaumer
    child.stdin.close()
466 0963d545 Renรฉ Nussbaumer
    poller.register(child.stdout, select.POLLIN)
467 0963d545 Renรฉ Nussbaumer
    poller.register(child.stderr, select.POLLIN)
468 0963d545 Renรฉ Nussbaumer
    fdmap = {
469 0963d545 Renรฉ Nussbaumer
      child.stdout.fileno(): (out, child.stdout),
470 0963d545 Renรฉ Nussbaumer
      child.stderr.fileno(): (err, child.stderr),
471 0963d545 Renรฉ Nussbaumer
      }
472 0963d545 Renรฉ Nussbaumer
    for fd in fdmap:
473 0963d545 Renรฉ Nussbaumer
      SetNonblockFlag(fd, True)
474 0963d545 Renรฉ Nussbaumer
475 0963d545 Renรฉ Nussbaumer
    while fdmap:
476 0963d545 Renรฉ Nussbaumer
      pollresult = RetryOnSignal(poller.poll)
477 0963d545 Renรฉ Nussbaumer
478 0963d545 Renรฉ Nussbaumer
      for fd, event in pollresult:
479 0963d545 Renรฉ Nussbaumer
        if event & select.POLLIN or event & select.POLLPRI:
480 0963d545 Renรฉ Nussbaumer
          data = fdmap[fd][1].read()
481 0963d545 Renรฉ Nussbaumer
          # no data from read signifies EOF (the same as POLLHUP)
482 0963d545 Renรฉ Nussbaumer
          if not data:
483 0963d545 Renรฉ Nussbaumer
            poller.unregister(fd)
484 0963d545 Renรฉ Nussbaumer
            del fdmap[fd]
485 0963d545 Renรฉ Nussbaumer
            continue
486 0963d545 Renรฉ Nussbaumer
          fdmap[fd][0].write(data)
487 0963d545 Renรฉ Nussbaumer
        if (event & select.POLLNVAL or event & select.POLLHUP or
488 0963d545 Renรฉ Nussbaumer
            event & select.POLLERR):
489 9c233417 Iustin Pop
          poller.unregister(fd)
490 9c233417 Iustin Pop
          del fdmap[fd]
491 9c233417 Iustin Pop
492 9c233417 Iustin Pop
  out = out.getvalue()
493 9c233417 Iustin Pop
  err = err.getvalue()
494 a8083063 Iustin Pop
495 a8083063 Iustin Pop
  status = child.wait()
496 36117c2b Iustin Pop
  return out, err, status
497 a8083063 Iustin Pop
498 36117c2b Iustin Pop
499 8797df43 Iustin Pop
def _RunCmdFile(cmd, env, via_shell, output, cwd):
500 36117c2b Iustin Pop
  """Run a command and save its output to a file.
501 36117c2b Iustin Pop

502 36117c2b Iustin Pop
  @type  cmd: string or list
503 36117c2b Iustin Pop
  @param cmd: Command to run
504 36117c2b Iustin Pop
  @type env: dict
505 36117c2b Iustin Pop
  @param env: The environment to use
506 36117c2b Iustin Pop
  @type via_shell: bool
507 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
508 36117c2b Iustin Pop
  @type output: str
509 36117c2b Iustin Pop
  @param output: the filename in which to save the output
510 8797df43 Iustin Pop
  @type cwd: string
511 8797df43 Iustin Pop
  @param cwd: the working directory for the program
512 36117c2b Iustin Pop
  @rtype: int
513 36117c2b Iustin Pop
  @return: the exit status
514 36117c2b Iustin Pop

515 36117c2b Iustin Pop
  """
516 36117c2b Iustin Pop
  fh = open(output, "a")
517 36117c2b Iustin Pop
  try:
518 36117c2b Iustin Pop
    child = subprocess.Popen(cmd, shell=via_shell,
519 36117c2b Iustin Pop
                             stderr=subprocess.STDOUT,
520 36117c2b Iustin Pop
                             stdout=fh,
521 36117c2b Iustin Pop
                             stdin=subprocess.PIPE,
522 8797df43 Iustin Pop
                             close_fds=True, env=env,
523 8797df43 Iustin Pop
                             cwd=cwd)
524 36117c2b Iustin Pop
525 36117c2b Iustin Pop
    child.stdin.close()
526 36117c2b Iustin Pop
    status = child.wait()
527 36117c2b Iustin Pop
  finally:
528 36117c2b Iustin Pop
    fh.close()
529 36117c2b Iustin Pop
  return status
530 a8083063 Iustin Pop
531 a8083063 Iustin Pop
532 73027ed2 Michael Hanselmann
def SetCloseOnExecFlag(fd, enable):
533 73027ed2 Michael Hanselmann
  """Sets or unsets the close-on-exec flag on a file descriptor.
534 73027ed2 Michael Hanselmann

535 73027ed2 Michael Hanselmann
  @type fd: int
536 73027ed2 Michael Hanselmann
  @param fd: File descriptor
537 73027ed2 Michael Hanselmann
  @type enable: bool
538 73027ed2 Michael Hanselmann
  @param enable: Whether to set or unset it.
539 73027ed2 Michael Hanselmann

540 73027ed2 Michael Hanselmann
  """
541 73027ed2 Michael Hanselmann
  flags = fcntl.fcntl(fd, fcntl.F_GETFD)
542 73027ed2 Michael Hanselmann
543 73027ed2 Michael Hanselmann
  if enable:
544 73027ed2 Michael Hanselmann
    flags |= fcntl.FD_CLOEXEC
545 73027ed2 Michael Hanselmann
  else:
546 73027ed2 Michael Hanselmann
    flags &= ~fcntl.FD_CLOEXEC
547 73027ed2 Michael Hanselmann
548 73027ed2 Michael Hanselmann
  fcntl.fcntl(fd, fcntl.F_SETFD, flags)
549 73027ed2 Michael Hanselmann
550 73027ed2 Michael Hanselmann
551 287a1740 Michael Hanselmann
def SetNonblockFlag(fd, enable):
552 287a1740 Michael Hanselmann
  """Sets or unsets the O_NONBLOCK flag on on a file descriptor.
553 287a1740 Michael Hanselmann

554 287a1740 Michael Hanselmann
  @type fd: int
555 287a1740 Michael Hanselmann
  @param fd: File descriptor
556 287a1740 Michael Hanselmann
  @type enable: bool
557 287a1740 Michael Hanselmann
  @param enable: Whether to set or unset it
558 287a1740 Michael Hanselmann

559 287a1740 Michael Hanselmann
  """
560 287a1740 Michael Hanselmann
  flags = fcntl.fcntl(fd, fcntl.F_GETFL)
561 287a1740 Michael Hanselmann
562 287a1740 Michael Hanselmann
  if enable:
563 287a1740 Michael Hanselmann
    flags |= os.O_NONBLOCK
564 287a1740 Michael Hanselmann
  else:
565 287a1740 Michael Hanselmann
    flags &= ~os.O_NONBLOCK
566 287a1740 Michael Hanselmann
567 287a1740 Michael Hanselmann
  fcntl.fcntl(fd, fcntl.F_SETFL, flags)
568 287a1740 Michael Hanselmann
569 287a1740 Michael Hanselmann
570 edcb5d9e Michael Hanselmann
def RetryOnSignal(fn, *args, **kwargs):
571 edcb5d9e Michael Hanselmann
  """Calls a function again if it failed due to EINTR.
572 edcb5d9e Michael Hanselmann

573 edcb5d9e Michael Hanselmann
  """
574 edcb5d9e Michael Hanselmann
  while True:
575 edcb5d9e Michael Hanselmann
    try:
576 edcb5d9e Michael Hanselmann
      return fn(*args, **kwargs)
577 965d0e5b Guido Trotter
    except EnvironmentError, err:
578 edcb5d9e Michael Hanselmann
      if err.errno != errno.EINTR:
579 edcb5d9e Michael Hanselmann
        raise
580 965d0e5b Guido Trotter
    except (socket.error, select.error), err:
581 965d0e5b Guido Trotter
      # In python 2.6 and above select.error is an IOError, so it's handled
582 965d0e5b Guido Trotter
      # above, in 2.5 and below it's not, and it's handled here.
583 edcb5d9e Michael Hanselmann
      if not (err.args and err.args[0] == errno.EINTR):
584 edcb5d9e Michael Hanselmann
        raise
585 edcb5d9e Michael Hanselmann
586 edcb5d9e Michael Hanselmann
587 6bb65e3a Guido Trotter
def RunParts(dir_name, env=None, reset_env=False):
588 6bb65e3a Guido Trotter
  """Run Scripts or programs in a directory
589 6bb65e3a Guido Trotter

590 6bb65e3a Guido Trotter
  @type dir_name: string
591 6bb65e3a Guido Trotter
  @param dir_name: absolute path to a directory
592 6bb65e3a Guido Trotter
  @type env: dict
593 6bb65e3a Guido Trotter
  @param env: The environment to use
594 6bb65e3a Guido Trotter
  @type reset_env: boolean
595 6bb65e3a Guido Trotter
  @param reset_env: whether to reset or keep the default os environment
596 6bb65e3a Guido Trotter
  @rtype: list of tuples
597 6bb65e3a Guido Trotter
  @return: list of (name, (one of RUNDIR_STATUS), RunResult)
598 6bb65e3a Guido Trotter

599 6bb65e3a Guido Trotter
  """
600 6bb65e3a Guido Trotter
  rr = []
601 6bb65e3a Guido Trotter
602 6bb65e3a Guido Trotter
  try:
603 6bb65e3a Guido Trotter
    dir_contents = ListVisibleFiles(dir_name)
604 6bb65e3a Guido Trotter
  except OSError, err:
605 6bb65e3a Guido Trotter
    logging.warning("RunParts: skipping %s (cannot list: %s)", dir_name, err)
606 6bb65e3a Guido Trotter
    return rr
607 6bb65e3a Guido Trotter
608 6bb65e3a Guido Trotter
  for relname in sorted(dir_contents):
609 c4feafe8 Iustin Pop
    fname = PathJoin(dir_name, relname)
610 6bb65e3a Guido Trotter
    if not (os.path.isfile(fname) and os.access(fname, os.X_OK) and
611 6bb65e3a Guido Trotter
            constants.EXT_PLUGIN_MASK.match(relname) is not None):
612 6bb65e3a Guido Trotter
      rr.append((relname, constants.RUNPARTS_SKIP, None))
613 6bb65e3a Guido Trotter
    else:
614 6bb65e3a Guido Trotter
      try:
615 6bb65e3a Guido Trotter
        result = RunCmd([fname], env=env, reset_env=reset_env)
616 6bb65e3a Guido Trotter
      except Exception, err: # pylint: disable-msg=W0703
617 6bb65e3a Guido Trotter
        rr.append((relname, constants.RUNPARTS_ERR, str(err)))
618 6bb65e3a Guido Trotter
      else:
619 6bb65e3a Guido Trotter
        rr.append((relname, constants.RUNPARTS_RUN, result))
620 6bb65e3a Guido Trotter
621 6bb65e3a Guido Trotter
  return rr
622 6bb65e3a Guido Trotter
623 6bb65e3a Guido Trotter
624 a8083063 Iustin Pop
def RemoveFile(filename):
625 a8083063 Iustin Pop
  """Remove a file ignoring some errors.
626 a8083063 Iustin Pop

627 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
628 a8083063 Iustin Pop
  errors are passed.
629 a8083063 Iustin Pop

630 58885d79 Iustin Pop
  @type filename: str
631 58885d79 Iustin Pop
  @param filename: the file to be removed
632 58885d79 Iustin Pop

633 a8083063 Iustin Pop
  """
634 a8083063 Iustin Pop
  try:
635 a8083063 Iustin Pop
    os.unlink(filename)
636 a8083063 Iustin Pop
  except OSError, err:
637 4ca1b175 Alexander Schreiber
    if err.errno not in (errno.ENOENT, errno.EISDIR):
638 a8083063 Iustin Pop
      raise
639 a8083063 Iustin Pop
640 72087dcd Balazs Lecz
641 72087dcd Balazs Lecz
def RemoveDir(dirname):
642 72087dcd Balazs Lecz
  """Remove an empty directory.
643 72087dcd Balazs Lecz

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

648 72087dcd Balazs Lecz
  @type dirname: str
649 72087dcd Balazs Lecz
  @param dirname: the empty directory to be removed
650 72087dcd Balazs Lecz

651 72087dcd Balazs Lecz
  """
652 72087dcd Balazs Lecz
  try:
653 72087dcd Balazs Lecz
    os.rmdir(dirname)
654 72087dcd Balazs Lecz
  except OSError, err:
655 72087dcd Balazs Lecz
    if err.errno != errno.ENOENT:
656 72087dcd Balazs Lecz
      raise
657 72087dcd Balazs Lecz
658 a8083063 Iustin Pop
659 6e797216 Michael Hanselmann
def RenameFile(old, new, mkdir=False, mkdir_mode=0750):
660 6e797216 Michael Hanselmann
  """Renames a file.
661 6e797216 Michael Hanselmann

662 6e797216 Michael Hanselmann
  @type old: string
663 6e797216 Michael Hanselmann
  @param old: Original path
664 6e797216 Michael Hanselmann
  @type new: string
665 6e797216 Michael Hanselmann
  @param new: New path
666 6e797216 Michael Hanselmann
  @type mkdir: bool
667 6e797216 Michael Hanselmann
  @param mkdir: Whether to create target directory if it doesn't exist
668 6e797216 Michael Hanselmann
  @type mkdir_mode: int
669 6e797216 Michael Hanselmann
  @param mkdir_mode: Mode for newly created directories
670 6e797216 Michael Hanselmann

671 6e797216 Michael Hanselmann
  """
672 6e797216 Michael Hanselmann
  try:
673 6e797216 Michael Hanselmann
    return os.rename(old, new)
674 6e797216 Michael Hanselmann
  except OSError, err:
675 6e797216 Michael Hanselmann
    # In at least one use case of this function, the job queue, directory
676 6e797216 Michael Hanselmann
    # creation is very rare. Checking for the directory before renaming is not
677 6e797216 Michael Hanselmann
    # as efficient.
678 6e797216 Michael Hanselmann
    if mkdir and err.errno == errno.ENOENT:
679 6e797216 Michael Hanselmann
      # Create directory and try again
680 cc2f004d Michael Hanselmann
      Makedirs(os.path.dirname(new), mode=mkdir_mode)
681 a426508d Michael Hanselmann
682 6e797216 Michael Hanselmann
      return os.rename(old, new)
683 a426508d Michael Hanselmann
684 6e797216 Michael Hanselmann
    raise
685 6e797216 Michael Hanselmann
686 6e797216 Michael Hanselmann
687 76e5f8b5 Michael Hanselmann
def Makedirs(path, mode=0750):
688 76e5f8b5 Michael Hanselmann
  """Super-mkdir; create a leaf directory and all intermediate ones.
689 76e5f8b5 Michael Hanselmann

690 76e5f8b5 Michael Hanselmann
  This is a wrapper around C{os.makedirs} adding error handling not implemented
691 76e5f8b5 Michael Hanselmann
  before Python 2.5.
692 76e5f8b5 Michael Hanselmann

693 76e5f8b5 Michael Hanselmann
  """
694 76e5f8b5 Michael Hanselmann
  try:
695 76e5f8b5 Michael Hanselmann
    os.makedirs(path, mode)
696 76e5f8b5 Michael Hanselmann
  except OSError, err:
697 76e5f8b5 Michael Hanselmann
    # Ignore EEXIST. This is only handled in os.makedirs as included in
698 76e5f8b5 Michael Hanselmann
    # Python 2.5 and above.
699 76e5f8b5 Michael Hanselmann
    if err.errno != errno.EEXIST or not os.path.exists(path):
700 76e5f8b5 Michael Hanselmann
      raise
701 76e5f8b5 Michael Hanselmann
702 76e5f8b5 Michael Hanselmann
703 055f822b Michael Hanselmann
def ResetTempfileModule():
704 055f822b Michael Hanselmann
  """Resets the random name generator of the tempfile module.
705 055f822b Michael Hanselmann

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

712 055f822b Michael Hanselmann
  """
713 055f822b Michael Hanselmann
  # pylint: disable-msg=W0212
714 055f822b Michael Hanselmann
  if hasattr(tempfile, "_once_lock") and hasattr(tempfile, "_name_sequence"):
715 055f822b Michael Hanselmann
    tempfile._once_lock.acquire()
716 055f822b Michael Hanselmann
    try:
717 055f822b Michael Hanselmann
      # Reset random name generator
718 055f822b Michael Hanselmann
      tempfile._name_sequence = None
719 055f822b Michael Hanselmann
    finally:
720 055f822b Michael Hanselmann
      tempfile._once_lock.release()
721 055f822b Michael Hanselmann
  else:
722 055f822b Michael Hanselmann
    logging.critical("The tempfile module misses at least one of the"
723 055f822b Michael Hanselmann
                     " '_once_lock' and '_name_sequence' attributes")
724 055f822b Michael Hanselmann
725 055f822b Michael Hanselmann
726 a8083063 Iustin Pop
def _FingerprintFile(filename):
727 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
728 a8083063 Iustin Pop

729 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
730 a8083063 Iustin Pop
  instead.
731 a8083063 Iustin Pop

732 58885d79 Iustin Pop
  @type filename: str
733 58885d79 Iustin Pop
  @param filename: the filename to checksum
734 58885d79 Iustin Pop
  @rtype: str
735 58885d79 Iustin Pop
  @return: the hex digest of the sha checksum of the contents
736 58885d79 Iustin Pop
      of the file
737 a8083063 Iustin Pop

738 a8083063 Iustin Pop
  """
739 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
740 a8083063 Iustin Pop
    return None
741 a8083063 Iustin Pop
742 a8083063 Iustin Pop
  f = open(filename)
743 a8083063 Iustin Pop
744 716a32cb Guido Trotter
  fp = compat.sha1_hash()
745 a8083063 Iustin Pop
  while True:
746 a8083063 Iustin Pop
    data = f.read(4096)
747 a8083063 Iustin Pop
    if not data:
748 a8083063 Iustin Pop
      break
749 a8083063 Iustin Pop
750 a8083063 Iustin Pop
    fp.update(data)
751 a8083063 Iustin Pop
752 a8083063 Iustin Pop
  return fp.hexdigest()
753 a8083063 Iustin Pop
754 a8083063 Iustin Pop
755 a8083063 Iustin Pop
def FingerprintFiles(files):
756 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
757 a8083063 Iustin Pop

758 58885d79 Iustin Pop
  @type files: list
759 58885d79 Iustin Pop
  @param files: the list of filename to fingerprint
760 58885d79 Iustin Pop
  @rtype: dict
761 58885d79 Iustin Pop
  @return: a dictionary filename: fingerprint, holding only
762 58885d79 Iustin Pop
      existing files
763 a8083063 Iustin Pop

764 a8083063 Iustin Pop
  """
765 a8083063 Iustin Pop
  ret = {}
766 a8083063 Iustin Pop
767 a8083063 Iustin Pop
  for filename in files:
768 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
769 a8083063 Iustin Pop
    if cksum:
770 a8083063 Iustin Pop
      ret[filename] = cksum
771 a8083063 Iustin Pop
772 a8083063 Iustin Pop
  return ret
773 a8083063 Iustin Pop
774 a8083063 Iustin Pop
775 a5728081 Guido Trotter
def ForceDictType(target, key_types, allowed_values=None):
776 a5728081 Guido Trotter
  """Force the values of a dict to have certain types.
777 a5728081 Guido Trotter

778 a5728081 Guido Trotter
  @type target: dict
779 a5728081 Guido Trotter
  @param target: the dict to update
780 a5728081 Guido Trotter
  @type key_types: dict
781 a5728081 Guido Trotter
  @param key_types: dict mapping target dict keys to types
782 a5728081 Guido Trotter
                    in constants.ENFORCEABLE_TYPES
783 a5728081 Guido Trotter
  @type allowed_values: list
784 a5728081 Guido Trotter
  @keyword allowed_values: list of specially allowed values
785 a5728081 Guido Trotter

786 a5728081 Guido Trotter
  """
787 a5728081 Guido Trotter
  if allowed_values is None:
788 a5728081 Guido Trotter
    allowed_values = []
789 a5728081 Guido Trotter
790 8b46606c Guido Trotter
  if not isinstance(target, dict):
791 8b46606c Guido Trotter
    msg = "Expected dictionary, got '%s'" % target
792 8b46606c Guido Trotter
    raise errors.TypeEnforcementError(msg)
793 8b46606c Guido Trotter
794 a5728081 Guido Trotter
  for key in target:
795 a5728081 Guido Trotter
    if key not in key_types:
796 a5728081 Guido Trotter
      msg = "Unknown key '%s'" % key
797 a5728081 Guido Trotter
      raise errors.TypeEnforcementError(msg)
798 a5728081 Guido Trotter
799 a5728081 Guido Trotter
    if target[key] in allowed_values:
800 a5728081 Guido Trotter
      continue
801 a5728081 Guido Trotter
802 29921401 Iustin Pop
    ktype = key_types[key]
803 29921401 Iustin Pop
    if ktype not in constants.ENFORCEABLE_TYPES:
804 29921401 Iustin Pop
      msg = "'%s' has non-enforceable type %s" % (key, ktype)
805 a5728081 Guido Trotter
      raise errors.ProgrammerError(msg)
806 a5728081 Guido Trotter
807 59525e1f Michael Hanselmann
    if ktype in (constants.VTYPE_STRING, constants.VTYPE_MAYBE_STRING):
808 59525e1f Michael Hanselmann
      if target[key] is None and ktype == constants.VTYPE_MAYBE_STRING:
809 59525e1f Michael Hanselmann
        pass
810 59525e1f Michael Hanselmann
      elif not isinstance(target[key], basestring):
811 a5728081 Guido Trotter
        if isinstance(target[key], bool) and not target[key]:
812 a5728081 Guido Trotter
          target[key] = ''
813 a5728081 Guido Trotter
        else:
814 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid string" % (key, target[key])
815 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
816 29921401 Iustin Pop
    elif ktype == constants.VTYPE_BOOL:
817 a5728081 Guido Trotter
      if isinstance(target[key], basestring) and target[key]:
818 a5728081 Guido Trotter
        if target[key].lower() == constants.VALUE_FALSE:
819 a5728081 Guido Trotter
          target[key] = False
820 a5728081 Guido Trotter
        elif target[key].lower() == constants.VALUE_TRUE:
821 a5728081 Guido Trotter
          target[key] = True
822 a5728081 Guido Trotter
        else:
823 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid boolean" % (key, target[key])
824 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
825 a5728081 Guido Trotter
      elif target[key]:
826 a5728081 Guido Trotter
        target[key] = True
827 a5728081 Guido Trotter
      else:
828 a5728081 Guido Trotter
        target[key] = False
829 29921401 Iustin Pop
    elif ktype == constants.VTYPE_SIZE:
830 a5728081 Guido Trotter
      try:
831 a5728081 Guido Trotter
        target[key] = ParseUnit(target[key])
832 a5728081 Guido Trotter
      except errors.UnitParseError, err:
833 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid size. error: %s" % \
834 a5728081 Guido Trotter
              (key, target[key], err)
835 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
836 29921401 Iustin Pop
    elif ktype == constants.VTYPE_INT:
837 a5728081 Guido Trotter
      try:
838 a5728081 Guido Trotter
        target[key] = int(target[key])
839 a5728081 Guido Trotter
      except (ValueError, TypeError):
840 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid integer" % (key, target[key])
841 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
842 a5728081 Guido Trotter
843 a5728081 Guido Trotter
844 a01b500b Michael Hanselmann
def _GetProcStatusPath(pid):
845 a01b500b Michael Hanselmann
  """Returns the path for a PID's proc status file.
846 a01b500b Michael Hanselmann

847 a01b500b Michael Hanselmann
  @type pid: int
848 a01b500b Michael Hanselmann
  @param pid: Process ID
849 a01b500b Michael Hanselmann
  @rtype: string
850 a01b500b Michael Hanselmann

851 a01b500b Michael Hanselmann
  """
852 a01b500b Michael Hanselmann
  return "/proc/%d/status" % pid
853 a01b500b Michael Hanselmann
854 a01b500b Michael Hanselmann
855 a8083063 Iustin Pop
def IsProcessAlive(pid):
856 a8083063 Iustin Pop
  """Check if a given pid exists on the system.
857 a8083063 Iustin Pop

858 44bf25ff Iustin Pop
  @note: zombie status is not handled, so zombie processes
859 44bf25ff Iustin Pop
      will be returned as alive
860 58885d79 Iustin Pop
  @type pid: int
861 58885d79 Iustin Pop
  @param pid: the process ID to check
862 58885d79 Iustin Pop
  @rtype: boolean
863 58885d79 Iustin Pop
  @return: True if the process exists
864 a8083063 Iustin Pop

865 a8083063 Iustin Pop
  """
866 5ef5ea45 Guido Trotter
  def _TryStat(name):
867 5ef5ea45 Guido Trotter
    try:
868 5ef5ea45 Guido Trotter
      os.stat(name)
869 5ef5ea45 Guido Trotter
      return True
870 5ef5ea45 Guido Trotter
    except EnvironmentError, err:
871 5ef5ea45 Guido Trotter
      if err.errno in (errno.ENOENT, errno.ENOTDIR):
872 5ef5ea45 Guido Trotter
        return False
873 5ef5ea45 Guido Trotter
      elif err.errno == errno.EINVAL:
874 5ef5ea45 Guido Trotter
        raise RetryAgain(err)
875 5ef5ea45 Guido Trotter
      raise
876 5ef5ea45 Guido Trotter
877 5ef5ea45 Guido Trotter
  assert isinstance(pid, int), "pid must be an integer"
878 d9f311d7 Iustin Pop
  if pid <= 0:
879 d9f311d7 Iustin Pop
    return False
880 d9f311d7 Iustin Pop
881 5ef5ea45 Guido Trotter
  # /proc in a multiprocessor environment can have strange behaviors.
882 5ef5ea45 Guido Trotter
  # Retry the os.stat a few times until we get a good result.
883 a8083063 Iustin Pop
  try:
884 a01b500b Michael Hanselmann
    return Retry(_TryStat, (0.01, 1.5, 0.1), 0.5,
885 a01b500b Michael Hanselmann
                 args=[_GetProcStatusPath(pid)])
886 5ef5ea45 Guido Trotter
  except RetryTimeout, err:
887 5ef5ea45 Guido Trotter
    err.RaiseInner()
888 a8083063 Iustin Pop
889 a8083063 Iustin Pop
890 a01b500b Michael Hanselmann
def _ParseSigsetT(sigset):
891 a01b500b Michael Hanselmann
  """Parse a rendered sigset_t value.
892 a01b500b Michael Hanselmann

893 a01b500b Michael Hanselmann
  This is the opposite of the Linux kernel's fs/proc/array.c:render_sigset_t
894 a01b500b Michael Hanselmann
  function.
895 a01b500b Michael Hanselmann

896 a01b500b Michael Hanselmann
  @type sigset: string
897 a01b500b Michael Hanselmann
  @param sigset: Rendered signal set from /proc/$pid/status
898 a01b500b Michael Hanselmann
  @rtype: set
899 a01b500b Michael Hanselmann
  @return: Set of all enabled signal numbers
900 a01b500b Michael Hanselmann

901 a01b500b Michael Hanselmann
  """
902 a01b500b Michael Hanselmann
  result = set()
903 a01b500b Michael Hanselmann
904 a01b500b Michael Hanselmann
  signum = 0
905 a01b500b Michael Hanselmann
  for ch in reversed(sigset):
906 a01b500b Michael Hanselmann
    chv = int(ch, 16)
907 a01b500b Michael Hanselmann
908 a01b500b Michael Hanselmann
    # The following could be done in a loop, but it's easier to read and
909 a01b500b Michael Hanselmann
    # understand in the unrolled form
910 a01b500b Michael Hanselmann
    if chv & 1:
911 a01b500b Michael Hanselmann
      result.add(signum + 1)
912 a01b500b Michael Hanselmann
    if chv & 2:
913 a01b500b Michael Hanselmann
      result.add(signum + 2)
914 a01b500b Michael Hanselmann
    if chv & 4:
915 a01b500b Michael Hanselmann
      result.add(signum + 3)
916 a01b500b Michael Hanselmann
    if chv & 8:
917 a01b500b Michael Hanselmann
      result.add(signum + 4)
918 a01b500b Michael Hanselmann
919 a01b500b Michael Hanselmann
    signum += 4
920 a01b500b Michael Hanselmann
921 a01b500b Michael Hanselmann
  return result
922 a01b500b Michael Hanselmann
923 a01b500b Michael Hanselmann
924 a01b500b Michael Hanselmann
def _GetProcStatusField(pstatus, field):
925 a01b500b Michael Hanselmann
  """Retrieves a field from the contents of a proc status file.
926 a01b500b Michael Hanselmann

927 a01b500b Michael Hanselmann
  @type pstatus: string
928 a01b500b Michael Hanselmann
  @param pstatus: Contents of /proc/$pid/status
929 a01b500b Michael Hanselmann
  @type field: string
930 a01b500b Michael Hanselmann
  @param field: Name of field whose value should be returned
931 a01b500b Michael Hanselmann
  @rtype: string
932 a01b500b Michael Hanselmann

933 a01b500b Michael Hanselmann
  """
934 a01b500b Michael Hanselmann
  for line in pstatus.splitlines():
935 a01b500b Michael Hanselmann
    parts = line.split(":", 1)
936 a01b500b Michael Hanselmann
937 a01b500b Michael Hanselmann
    if len(parts) < 2 or parts[0] != field:
938 a01b500b Michael Hanselmann
      continue
939 a01b500b Michael Hanselmann
940 a01b500b Michael Hanselmann
    return parts[1].strip()
941 a01b500b Michael Hanselmann
942 a01b500b Michael Hanselmann
  return None
943 a01b500b Michael Hanselmann
944 a01b500b Michael Hanselmann
945 a01b500b Michael Hanselmann
def IsProcessHandlingSignal(pid, signum, status_path=None):
946 a01b500b Michael Hanselmann
  """Checks whether a process is handling a signal.
947 a01b500b Michael Hanselmann

948 a01b500b Michael Hanselmann
  @type pid: int
949 a01b500b Michael Hanselmann
  @param pid: Process ID
950 a01b500b Michael Hanselmann
  @type signum: int
951 a01b500b Michael Hanselmann
  @param signum: Signal number
952 a01b500b Michael Hanselmann
  @rtype: bool
953 a01b500b Michael Hanselmann

954 a01b500b Michael Hanselmann
  """
955 a01b500b Michael Hanselmann
  if status_path is None:
956 a01b500b Michael Hanselmann
    status_path = _GetProcStatusPath(pid)
957 a01b500b Michael Hanselmann
958 a01b500b Michael Hanselmann
  try:
959 a01b500b Michael Hanselmann
    proc_status = ReadFile(status_path)
960 a01b500b Michael Hanselmann
  except EnvironmentError, err:
961 a01b500b Michael Hanselmann
    # In at least one case, reading /proc/$pid/status failed with ESRCH.
962 a01b500b Michael Hanselmann
    if err.errno in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL, errno.ESRCH):
963 a01b500b Michael Hanselmann
      return False
964 a01b500b Michael Hanselmann
    raise
965 a01b500b Michael Hanselmann
966 a01b500b Michael Hanselmann
  sigcgt = _GetProcStatusField(proc_status, "SigCgt")
967 a01b500b Michael Hanselmann
  if sigcgt is None:
968 a01b500b Michael Hanselmann
    raise RuntimeError("%s is missing 'SigCgt' field" % status_path)
969 a01b500b Michael Hanselmann
970 a01b500b Michael Hanselmann
  # Now check whether signal is handled
971 a01b500b Michael Hanselmann
  return signum in _ParseSigsetT(sigcgt)
972 a01b500b Michael Hanselmann
973 a01b500b Michael Hanselmann
974 d9f311d7 Iustin Pop
def ReadPidFile(pidfile):
975 58885d79 Iustin Pop
  """Read a pid from a file.
976 fee80e90 Guido Trotter

977 58885d79 Iustin Pop
  @type  pidfile: string
978 58885d79 Iustin Pop
  @param pidfile: path to the file containing the pid
979 58885d79 Iustin Pop
  @rtype: int
980 1de62f37 Guido Trotter
  @return: The process id, if the file exists and contains a valid PID,
981 d9f311d7 Iustin Pop
           otherwise 0
982 fee80e90 Guido Trotter

983 fee80e90 Guido Trotter
  """
984 fee80e90 Guido Trotter
  try:
985 682f7601 Guido Trotter
    raw_data = ReadOneLineFile(pidfile)
986 d9f311d7 Iustin Pop
  except EnvironmentError, err:
987 d9f311d7 Iustin Pop
    if err.errno != errno.ENOENT:
988 13998ef2 Michael Hanselmann
      logging.exception("Can't read pid file")
989 d9f311d7 Iustin Pop
    return 0
990 fee80e90 Guido Trotter
991 fee80e90 Guido Trotter
  try:
992 13998ef2 Michael Hanselmann
    pid = int(raw_data)
993 691744c4 Iustin Pop
  except (TypeError, ValueError), err:
994 8161a646 Iustin Pop
    logging.info("Can't parse pid file contents", exc_info=True)
995 d9f311d7 Iustin Pop
    return 0
996 fee80e90 Guido Trotter
997 d9f311d7 Iustin Pop
  return pid
998 fee80e90 Guido Trotter
999 fee80e90 Guido Trotter
1000 debed9ae Michael Hanselmann
def ReadLockedPidFile(path):
1001 debed9ae Michael Hanselmann
  """Reads a locked PID file.
1002 debed9ae Michael Hanselmann

1003 debed9ae Michael Hanselmann
  This can be used together with L{StartDaemon}.
1004 debed9ae Michael Hanselmann

1005 debed9ae Michael Hanselmann
  @type path: string
1006 debed9ae Michael Hanselmann
  @param path: Path to PID file
1007 debed9ae Michael Hanselmann
  @return: PID as integer or, if file was unlocked or couldn't be opened, None
1008 debed9ae Michael Hanselmann

1009 debed9ae Michael Hanselmann
  """
1010 debed9ae Michael Hanselmann
  try:
1011 debed9ae Michael Hanselmann
    fd = os.open(path, os.O_RDONLY)
1012 debed9ae Michael Hanselmann
  except EnvironmentError, err:
1013 debed9ae Michael Hanselmann
    if err.errno == errno.ENOENT:
1014 debed9ae Michael Hanselmann
      # PID file doesn't exist
1015 debed9ae Michael Hanselmann
      return None
1016 debed9ae Michael Hanselmann
    raise
1017 debed9ae Michael Hanselmann
1018 debed9ae Michael Hanselmann
  try:
1019 debed9ae Michael Hanselmann
    try:
1020 debed9ae Michael Hanselmann
      # Try to acquire lock
1021 debed9ae Michael Hanselmann
      LockFile(fd)
1022 debed9ae Michael Hanselmann
    except errors.LockError:
1023 debed9ae Michael Hanselmann
      # Couldn't lock, daemon is running
1024 debed9ae Michael Hanselmann
      return int(os.read(fd, 100))
1025 debed9ae Michael Hanselmann
  finally:
1026 debed9ae Michael Hanselmann
    os.close(fd)
1027 debed9ae Michael Hanselmann
1028 debed9ae Michael Hanselmann
  return None
1029 debed9ae Michael Hanselmann
1030 debed9ae Michael Hanselmann
1031 256eb94b Guido Trotter
def MatchNameComponent(key, name_list, case_sensitive=True):
1032 a8083063 Iustin Pop
  """Try to match a name against a list.
1033 a8083063 Iustin Pop

1034 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
1035 58885d79 Iustin Pop
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
1036 58885d79 Iustin Pop
  this list, I{'test1'} as well as I{'test1.example'} will match, but
1037 58885d79 Iustin Pop
  not I{'test1.ex'}. A multiple match will be considered as no match
1038 58885d79 Iustin Pop
  at all (e.g. I{'test1'} against C{['test1.example.com',
1039 3a541d90 Iustin Pop
  'test1.example.org']}), except when the key fully matches an entry
1040 3a541d90 Iustin Pop
  (e.g. I{'test1'} against C{['test1', 'test1.example.com']}).
1041 a8083063 Iustin Pop

1042 58885d79 Iustin Pop
  @type key: str
1043 58885d79 Iustin Pop
  @param key: the name to be searched
1044 58885d79 Iustin Pop
  @type name_list: list
1045 58885d79 Iustin Pop
  @param name_list: the list of strings against which to search the key
1046 256eb94b Guido Trotter
  @type case_sensitive: boolean
1047 256eb94b Guido Trotter
  @param case_sensitive: whether to provide a case-sensitive match
1048 a8083063 Iustin Pop

1049 58885d79 Iustin Pop
  @rtype: None or str
1050 58885d79 Iustin Pop
  @return: None if there is no match I{or} if there are multiple matches,
1051 58885d79 Iustin Pop
      otherwise the element from the list which matches
1052 a8083063 Iustin Pop

1053 a8083063 Iustin Pop
  """
1054 3a541d90 Iustin Pop
  if key in name_list:
1055 3a541d90 Iustin Pop
    return key
1056 256eb94b Guido Trotter
1057 256eb94b Guido Trotter
  re_flags = 0
1058 256eb94b Guido Trotter
  if not case_sensitive:
1059 256eb94b Guido Trotter
    re_flags |= re.IGNORECASE
1060 099c52ad Iustin Pop
    key = key.upper()
1061 256eb94b Guido Trotter
  mo = re.compile("^%s(\..*)?$" % re.escape(key), re_flags)
1062 256eb94b Guido Trotter
  names_filtered = []
1063 256eb94b Guido Trotter
  string_matches = []
1064 256eb94b Guido Trotter
  for name in name_list:
1065 256eb94b Guido Trotter
    if mo.match(name) is not None:
1066 256eb94b Guido Trotter
      names_filtered.append(name)
1067 099c52ad Iustin Pop
      if not case_sensitive and key == name.upper():
1068 256eb94b Guido Trotter
        string_matches.append(name)
1069 256eb94b Guido Trotter
1070 256eb94b Guido Trotter
  if len(string_matches) == 1:
1071 256eb94b Guido Trotter
    return string_matches[0]
1072 256eb94b Guido Trotter
  if len(names_filtered) == 1:
1073 256eb94b Guido Trotter
    return names_filtered[0]
1074 256eb94b Guido Trotter
  return None
1075 a8083063 Iustin Pop
1076 a8083063 Iustin Pop
1077 28f34048 Michael Hanselmann
def ValidateServiceName(name):
1078 28f34048 Michael Hanselmann
  """Validate the given service name.
1079 28f34048 Michael Hanselmann

1080 28f34048 Michael Hanselmann
  @type name: number or string
1081 28f34048 Michael Hanselmann
  @param name: Service name or port specification
1082 28f34048 Michael Hanselmann

1083 28f34048 Michael Hanselmann
  """
1084 28f34048 Michael Hanselmann
  try:
1085 28f34048 Michael Hanselmann
    numport = int(name)
1086 28f34048 Michael Hanselmann
  except (ValueError, TypeError):
1087 28f34048 Michael Hanselmann
    # Non-numeric service name
1088 28f34048 Michael Hanselmann
    valid = _VALID_SERVICE_NAME_RE.match(name)
1089 28f34048 Michael Hanselmann
  else:
1090 28f34048 Michael Hanselmann
    # Numeric port (protocols other than TCP or UDP might need adjustments
1091 28f34048 Michael Hanselmann
    # here)
1092 28f34048 Michael Hanselmann
    valid = (numport >= 0 and numport < (1 << 16))
1093 28f34048 Michael Hanselmann
1094 28f34048 Michael Hanselmann
  if not valid:
1095 28f34048 Michael Hanselmann
    raise errors.OpPrereqError("Invalid service name '%s'" % name,
1096 28f34048 Michael Hanselmann
                               errors.ECODE_INVAL)
1097 28f34048 Michael Hanselmann
1098 28f34048 Michael Hanselmann
  return name
1099 28f34048 Michael Hanselmann
1100 28f34048 Michael Hanselmann
1101 a8083063 Iustin Pop
def ListVolumeGroups():
1102 a8083063 Iustin Pop
  """List volume groups and their size
1103 a8083063 Iustin Pop

1104 58885d79 Iustin Pop
  @rtype: dict
1105 58885d79 Iustin Pop
  @return:
1106 58885d79 Iustin Pop
       Dictionary with keys volume name and values
1107 58885d79 Iustin Pop
       the size of the volume
1108 a8083063 Iustin Pop

1109 a8083063 Iustin Pop
  """
1110 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
1111 a8083063 Iustin Pop
  result = RunCmd(command)
1112 a8083063 Iustin Pop
  retval = {}
1113 a8083063 Iustin Pop
  if result.failed:
1114 a8083063 Iustin Pop
    return retval
1115 a8083063 Iustin Pop
1116 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
1117 a8083063 Iustin Pop
    try:
1118 a8083063 Iustin Pop
      name, size = line.split()
1119 a8083063 Iustin Pop
      size = int(float(size))
1120 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
1121 bb698c1f Iustin Pop
      logging.error("Invalid output from vgs (%s): %s", err, line)
1122 a8083063 Iustin Pop
      continue
1123 a8083063 Iustin Pop
1124 a8083063 Iustin Pop
    retval[name] = size
1125 a8083063 Iustin Pop
1126 a8083063 Iustin Pop
  return retval
1127 a8083063 Iustin Pop
1128 a8083063 Iustin Pop
1129 a8083063 Iustin Pop
def BridgeExists(bridge):
1130 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
1131 a8083063 Iustin Pop

1132 58885d79 Iustin Pop
  @type bridge: str
1133 58885d79 Iustin Pop
  @param bridge: the bridge name to check
1134 58885d79 Iustin Pop
  @rtype: boolean
1135 58885d79 Iustin Pop
  @return: True if it does
1136 a8083063 Iustin Pop

1137 a8083063 Iustin Pop
  """
1138 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
1139 a8083063 Iustin Pop
1140 a8083063 Iustin Pop
1141 a8083063 Iustin Pop
def NiceSort(name_list):
1142 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
1143 a8083063 Iustin Pop

1144 58885d79 Iustin Pop
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
1145 58885d79 Iustin Pop
  will sort the list in the logical order C{['a1', 'a2', 'a10',
1146 58885d79 Iustin Pop
  'a11']}.
1147 a8083063 Iustin Pop

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

1152 58885d79 Iustin Pop
  @type name_list: list
1153 58885d79 Iustin Pop
  @param name_list: the names to be sorted
1154 58885d79 Iustin Pop
  @rtype: list
1155 58885d79 Iustin Pop
  @return: a copy of the name list sorted with our algorithm
1156 a8083063 Iustin Pop

1157 a8083063 Iustin Pop
  """
1158 a8083063 Iustin Pop
  _SORTER_BASE = "(\D+|\d+)"
1159 a8083063 Iustin Pop
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
1160 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
1161 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
1162 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE)
1163 a8083063 Iustin Pop
  _SORTER_RE = re.compile(_SORTER_FULL)
1164 a8083063 Iustin Pop
  _SORTER_NODIGIT = re.compile("^\D*$")
1165 a8083063 Iustin Pop
  def _TryInt(val):
1166 a8083063 Iustin Pop
    """Attempts to convert a variable to integer."""
1167 a8083063 Iustin Pop
    if val is None or _SORTER_NODIGIT.match(val):
1168 a8083063 Iustin Pop
      return val
1169 a8083063 Iustin Pop
    rval = int(val)
1170 a8083063 Iustin Pop
    return rval
1171 a8083063 Iustin Pop
1172 a8083063 Iustin Pop
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
1173 a8083063 Iustin Pop
             for name in name_list]
1174 a8083063 Iustin Pop
  to_sort.sort()
1175 a8083063 Iustin Pop
  return [tup[1] for tup in to_sort]
1176 a8083063 Iustin Pop
1177 a8083063 Iustin Pop
1178 a8083063 Iustin Pop
def TryConvert(fn, val):
1179 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
1180 a8083063 Iustin Pop

1181 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
1182 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
1183 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
1184 58885d79 Iustin Pop
  exceptions are propagated to the caller.
1185 58885d79 Iustin Pop

1186 58885d79 Iustin Pop
  @type fn: callable
1187 58885d79 Iustin Pop
  @param fn: function to apply to the value
1188 58885d79 Iustin Pop
  @param val: the value to be converted
1189 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
1190 58885d79 Iustin Pop
      otherwise the original value.
1191 a8083063 Iustin Pop

1192 a8083063 Iustin Pop
  """
1193 a8083063 Iustin Pop
  try:
1194 a8083063 Iustin Pop
    nv = fn(val)
1195 7c4d6c7b Michael Hanselmann
  except (ValueError, TypeError):
1196 a8083063 Iustin Pop
    nv = val
1197 a8083063 Iustin Pop
  return nv
1198 a8083063 Iustin Pop
1199 a8083063 Iustin Pop
1200 a8083063 Iustin Pop
def IsValidShellParam(word):
1201 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
1202 a8083063 Iustin Pop

1203 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
1204 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
1205 a8083063 Iustin Pop
  the actual command.
1206 a8083063 Iustin Pop

1207 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
1208 a8083063 Iustin Pop
  side.
1209 a8083063 Iustin Pop

1210 58885d79 Iustin Pop
  @type word: str
1211 58885d79 Iustin Pop
  @param word: the word to check
1212 58885d79 Iustin Pop
  @rtype: boolean
1213 58885d79 Iustin Pop
  @return: True if the word is 'safe'
1214 58885d79 Iustin Pop

1215 a8083063 Iustin Pop
  """
1216 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
1217 a8083063 Iustin Pop
1218 a8083063 Iustin Pop
1219 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
1220 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
1221 a8083063 Iustin Pop

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

1227 58885d79 Iustin Pop
  @type template: str
1228 58885d79 Iustin Pop
  @param template: the string holding the template for the
1229 58885d79 Iustin Pop
      string formatting
1230 58885d79 Iustin Pop
  @rtype: str
1231 58885d79 Iustin Pop
  @return: the expanded command line
1232 58885d79 Iustin Pop

1233 a8083063 Iustin Pop
  """
1234 a8083063 Iustin Pop
  for word in args:
1235 a8083063 Iustin Pop
    if not IsValidShellParam(word):
1236 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
1237 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
1238 a8083063 Iustin Pop
  return template % args
1239 a8083063 Iustin Pop
1240 a8083063 Iustin Pop
1241 9fbfbb7b Iustin Pop
def FormatUnit(value, units):
1242 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
1243 a8083063 Iustin Pop

1244 58885d79 Iustin Pop
  @type value: int
1245 58885d79 Iustin Pop
  @param value: integer representing the value in MiB (1048576)
1246 9fbfbb7b Iustin Pop
  @type units: char
1247 9fbfbb7b Iustin Pop
  @param units: the type of formatting we should do:
1248 9fbfbb7b Iustin Pop
      - 'h' for automatic scaling
1249 9fbfbb7b Iustin Pop
      - 'm' for MiBs
1250 9fbfbb7b Iustin Pop
      - 'g' for GiBs
1251 9fbfbb7b Iustin Pop
      - 't' for TiBs
1252 58885d79 Iustin Pop
  @rtype: str
1253 58885d79 Iustin Pop
  @return: the formatted value (with suffix)
1254 a8083063 Iustin Pop

1255 a8083063 Iustin Pop
  """
1256 9fbfbb7b Iustin Pop
  if units not in ('m', 'g', 't', 'h'):
1257 9fbfbb7b Iustin Pop
    raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
1258 a8083063 Iustin Pop
1259 9fbfbb7b Iustin Pop
  suffix = ''
1260 9fbfbb7b Iustin Pop
1261 9fbfbb7b Iustin Pop
  if units == 'm' or (units == 'h' and value < 1024):
1262 9fbfbb7b Iustin Pop
    if units == 'h':
1263 9fbfbb7b Iustin Pop
      suffix = 'M'
1264 9fbfbb7b Iustin Pop
    return "%d%s" % (round(value, 0), suffix)
1265 9fbfbb7b Iustin Pop
1266 9fbfbb7b Iustin Pop
  elif units == 'g' or (units == 'h' and value < (1024 * 1024)):
1267 9fbfbb7b Iustin Pop
    if units == 'h':
1268 9fbfbb7b Iustin Pop
      suffix = 'G'
1269 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024, 1), suffix)
1270 a8083063 Iustin Pop
1271 a8083063 Iustin Pop
  else:
1272 9fbfbb7b Iustin Pop
    if units == 'h':
1273 9fbfbb7b Iustin Pop
      suffix = 'T'
1274 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
1275 a8083063 Iustin Pop
1276 a8083063 Iustin Pop
1277 a8083063 Iustin Pop
def ParseUnit(input_string):
1278 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
1279 a8083063 Iustin Pop

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

1284 a8083063 Iustin Pop
  """
1285 9939547b Iustin Pop
  m = re.match('^([.\d]+)\s*([a-zA-Z]+)?$', str(input_string))
1286 a8083063 Iustin Pop
  if not m:
1287 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Invalid format")
1288 a8083063 Iustin Pop
1289 a8083063 Iustin Pop
  value = float(m.groups()[0])
1290 a8083063 Iustin Pop
1291 a8083063 Iustin Pop
  unit = m.groups()[1]
1292 a8083063 Iustin Pop
  if unit:
1293 a8083063 Iustin Pop
    lcunit = unit.lower()
1294 a8083063 Iustin Pop
  else:
1295 a8083063 Iustin Pop
    lcunit = 'm'
1296 a8083063 Iustin Pop
1297 a8083063 Iustin Pop
  if lcunit in ('m', 'mb', 'mib'):
1298 a8083063 Iustin Pop
    # Value already in MiB
1299 a8083063 Iustin Pop
    pass
1300 a8083063 Iustin Pop
1301 a8083063 Iustin Pop
  elif lcunit in ('g', 'gb', 'gib'):
1302 a8083063 Iustin Pop
    value *= 1024
1303 a8083063 Iustin Pop
1304 a8083063 Iustin Pop
  elif lcunit in ('t', 'tb', 'tib'):
1305 a8083063 Iustin Pop
    value *= 1024 * 1024
1306 a8083063 Iustin Pop
1307 a8083063 Iustin Pop
  else:
1308 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Unknown unit: %s" % unit)
1309 a8083063 Iustin Pop
1310 a8083063 Iustin Pop
  # Make sure we round up
1311 a8083063 Iustin Pop
  if int(value) < value:
1312 a8083063 Iustin Pop
    value += 1
1313 a8083063 Iustin Pop
1314 a8083063 Iustin Pop
  # Round up to the next multiple of 4
1315 a8083063 Iustin Pop
  value = int(value)
1316 a8083063 Iustin Pop
  if value % 4:
1317 a8083063 Iustin Pop
    value += 4 - value % 4
1318 a8083063 Iustin Pop
1319 a8083063 Iustin Pop
  return value
1320 a8083063 Iustin Pop
1321 a8083063 Iustin Pop
1322 31155d60 Balazs Lecz
def ParseCpuMask(cpu_mask):
1323 31155d60 Balazs Lecz
  """Parse a CPU mask definition and return the list of CPU IDs.
1324 31155d60 Balazs Lecz

1325 31155d60 Balazs Lecz
  CPU mask format: comma-separated list of CPU IDs
1326 31155d60 Balazs Lecz
  or dash-separated ID ranges
1327 31155d60 Balazs Lecz
  Example: "0-2,5" -> "0,1,2,5"
1328 31155d60 Balazs Lecz

1329 31155d60 Balazs Lecz
  @type cpu_mask: str
1330 31155d60 Balazs Lecz
  @param cpu_mask: CPU mask definition
1331 31155d60 Balazs Lecz
  @rtype: list of int
1332 31155d60 Balazs Lecz
  @return: list of CPU IDs
1333 31155d60 Balazs Lecz

1334 31155d60 Balazs Lecz
  """
1335 31155d60 Balazs Lecz
  if not cpu_mask:
1336 31155d60 Balazs Lecz
    return []
1337 31155d60 Balazs Lecz
  cpu_list = []
1338 31155d60 Balazs Lecz
  for range_def in cpu_mask.split(","):
1339 31155d60 Balazs Lecz
    boundaries = range_def.split("-")
1340 31155d60 Balazs Lecz
    n_elements = len(boundaries)
1341 31155d60 Balazs Lecz
    if n_elements > 2:
1342 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID range definition"
1343 31155d60 Balazs Lecz
                              " (only one hyphen allowed): %s" % range_def)
1344 31155d60 Balazs Lecz
    try:
1345 31155d60 Balazs Lecz
      lower = int(boundaries[0])
1346 31155d60 Balazs Lecz
    except (ValueError, TypeError), err:
1347 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID value for lower boundary of"
1348 31155d60 Balazs Lecz
                              " CPU ID range: %s" % str(err))
1349 31155d60 Balazs Lecz
    try:
1350 31155d60 Balazs Lecz
      higher = int(boundaries[-1])
1351 31155d60 Balazs Lecz
    except (ValueError, TypeError), err:
1352 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID value for higher boundary of"
1353 31155d60 Balazs Lecz
                              " CPU ID range: %s" % str(err))
1354 31155d60 Balazs Lecz
    if lower > higher:
1355 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID range definition"
1356 31155d60 Balazs Lecz
                              " (%d > %d): %s" % (lower, higher, range_def))
1357 31155d60 Balazs Lecz
    cpu_list.extend(range(lower, higher + 1))
1358 31155d60 Balazs Lecz
  return cpu_list
1359 31155d60 Balazs Lecz
1360 31155d60 Balazs Lecz
1361 3727671e Renรฉ Nussbaumer
def AddAuthorizedKey(file_obj, key):
1362 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
1363 a8083063 Iustin Pop

1364 3727671e Renรฉ Nussbaumer
  @type file_obj: str or file handle
1365 3727671e Renรฉ Nussbaumer
  @param file_obj: path to authorized_keys file
1366 58885d79 Iustin Pop
  @type key: str
1367 58885d79 Iustin Pop
  @param key: string containing key
1368 58885d79 Iustin Pop

1369 a8083063 Iustin Pop
  """
1370 a8083063 Iustin Pop
  key_fields = key.split()
1371 a8083063 Iustin Pop
1372 3727671e Renรฉ Nussbaumer
  if isinstance(file_obj, basestring):
1373 3727671e Renรฉ Nussbaumer
    f = open(file_obj, 'a+')
1374 3727671e Renรฉ Nussbaumer
  else:
1375 3727671e Renรฉ Nussbaumer
    f = file_obj
1376 3727671e Renรฉ Nussbaumer
1377 a8083063 Iustin Pop
  try:
1378 a8083063 Iustin Pop
    nl = True
1379 a8083063 Iustin Pop
    for line in f:
1380 a8083063 Iustin Pop
      # Ignore whitespace changes
1381 a8083063 Iustin Pop
      if line.split() == key_fields:
1382 a8083063 Iustin Pop
        break
1383 a8083063 Iustin Pop
      nl = line.endswith('\n')
1384 a8083063 Iustin Pop
    else:
1385 a8083063 Iustin Pop
      if not nl:
1386 a8083063 Iustin Pop
        f.write("\n")
1387 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
1388 a8083063 Iustin Pop
      f.write("\n")
1389 a8083063 Iustin Pop
      f.flush()
1390 a8083063 Iustin Pop
  finally:
1391 a8083063 Iustin Pop
    f.close()
1392 a8083063 Iustin Pop
1393 a8083063 Iustin Pop
1394 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
1395 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
1396 a8083063 Iustin Pop

1397 58885d79 Iustin Pop
  @type file_name: str
1398 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
1399 58885d79 Iustin Pop
  @type key: str
1400 58885d79 Iustin Pop
  @param key: string containing key
1401 58885d79 Iustin Pop

1402 a8083063 Iustin Pop
  """
1403 a8083063 Iustin Pop
  key_fields = key.split()
1404 a8083063 Iustin Pop
1405 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1406 a8083063 Iustin Pop
  try:
1407 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
1408 a8083063 Iustin Pop
    try:
1409 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
1410 59f82e3f Michael Hanselmann
      try:
1411 59f82e3f Michael Hanselmann
        for line in f:
1412 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
1413 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
1414 59f82e3f Michael Hanselmann
            out.write(line)
1415 899d2a81 Michael Hanselmann
1416 899d2a81 Michael Hanselmann
        out.flush()
1417 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
1418 899d2a81 Michael Hanselmann
      finally:
1419 899d2a81 Michael Hanselmann
        f.close()
1420 899d2a81 Michael Hanselmann
    finally:
1421 899d2a81 Michael Hanselmann
      out.close()
1422 899d2a81 Michael Hanselmann
  except:
1423 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
1424 899d2a81 Michael Hanselmann
    raise
1425 899d2a81 Michael Hanselmann
1426 899d2a81 Michael Hanselmann
1427 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
1428 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
1429 899d2a81 Michael Hanselmann

1430 58885d79 Iustin Pop
  @type file_name: str
1431 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1432 58885d79 Iustin Pop
  @type ip: str
1433 58885d79 Iustin Pop
  @param ip: the IP address
1434 58885d79 Iustin Pop
  @type hostname: str
1435 58885d79 Iustin Pop
  @param hostname: the hostname to be added
1436 58885d79 Iustin Pop
  @type aliases: list
1437 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
1438 58885d79 Iustin Pop

1439 899d2a81 Michael Hanselmann
  """
1440 7fbb1f65 Michael Hanselmann
  # Ensure aliases are unique
1441 7fbb1f65 Michael Hanselmann
  aliases = UniqueSequence([hostname] + aliases)[1:]
1442 7fbb1f65 Michael Hanselmann
1443 edcd876b Michael Hanselmann
  def _WriteEtcHosts(fd):
1444 edcd876b Michael Hanselmann
    # Duplicating file descriptor because os.fdopen's result will automatically
1445 edcd876b Michael Hanselmann
    # close the descriptor, but we would still like to have its functionality.
1446 edcd876b Michael Hanselmann
    out = os.fdopen(os.dup(fd), "w")
1447 9440aeab Michael Hanselmann
    try:
1448 edcd876b Michael Hanselmann
      for line in ReadFile(file_name).splitlines(True):
1449 edcd876b Michael Hanselmann
        fields = line.split()
1450 edcd876b Michael Hanselmann
        if fields and not fields[0].startswith("#") and ip == fields[0]:
1451 edcd876b Michael Hanselmann
          continue
1452 edcd876b Michael Hanselmann
        out.write(line)
1453 edcd876b Michael Hanselmann
1454 edcd876b Michael Hanselmann
      out.write("%s\t%s" % (ip, hostname))
1455 edcd876b Michael Hanselmann
      if aliases:
1456 edcd876b Michael Hanselmann
        out.write(" %s" % " ".join(aliases))
1457 edcd876b Michael Hanselmann
      out.write("\n")
1458 edcd876b Michael Hanselmann
      out.flush()
1459 9440aeab Michael Hanselmann
    finally:
1460 9440aeab Michael Hanselmann
      out.close()
1461 edcd876b Michael Hanselmann
1462 edcd876b Michael Hanselmann
  WriteFile(file_name, fn=_WriteEtcHosts, mode=0644)
1463 899d2a81 Michael Hanselmann
1464 899d2a81 Michael Hanselmann
1465 ea8ac9c9 Renรฉ Nussbaumer
def AddHostToEtcHosts(hostname, ip):
1466 d9c02ca6 Michael Hanselmann
  """Wrapper around SetEtcHostsEntry.
1467 d9c02ca6 Michael Hanselmann

1468 58885d79 Iustin Pop
  @type hostname: str
1469 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
1470 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1471 ea8ac9c9 Renรฉ Nussbaumer
  @type ip: str
1472 ea8ac9c9 Renรฉ Nussbaumer
  @param ip: The ip address of the host
1473 58885d79 Iustin Pop

1474 d9c02ca6 Michael Hanselmann
  """
1475 ea8ac9c9 Renรฉ Nussbaumer
  SetEtcHostsEntry(constants.ETC_HOSTS, ip, hostname, [hostname.split(".")[0]])
1476 d9c02ca6 Michael Hanselmann
1477 d9c02ca6 Michael Hanselmann
1478 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
1479 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
1480 899d2a81 Michael Hanselmann

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

1483 58885d79 Iustin Pop
  @type file_name: str
1484 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1485 58885d79 Iustin Pop
  @type hostname: str
1486 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
1487 58885d79 Iustin Pop

1488 899d2a81 Michael Hanselmann
  """
1489 edcd876b Michael Hanselmann
  def _WriteEtcHosts(fd):
1490 edcd876b Michael Hanselmann
    # Duplicating file descriptor because os.fdopen's result will automatically
1491 edcd876b Michael Hanselmann
    # close the descriptor, but we would still like to have its functionality.
1492 edcd876b Michael Hanselmann
    out = os.fdopen(os.dup(fd), "w")
1493 899d2a81 Michael Hanselmann
    try:
1494 edcd876b Michael Hanselmann
      for line in ReadFile(file_name).splitlines(True):
1495 edcd876b Michael Hanselmann
        fields = line.split()
1496 edcd876b Michael Hanselmann
        if len(fields) > 1 and not fields[0].startswith("#"):
1497 edcd876b Michael Hanselmann
          names = fields[1:]
1498 edcd876b Michael Hanselmann
          if hostname in names:
1499 edcd876b Michael Hanselmann
            while hostname in names:
1500 edcd876b Michael Hanselmann
              names.remove(hostname)
1501 edcd876b Michael Hanselmann
            if names:
1502 edcd876b Michael Hanselmann
              out.write("%s %s\n" % (fields[0], " ".join(names)))
1503 edcd876b Michael Hanselmann
            continue
1504 59f82e3f Michael Hanselmann
1505 edcd876b Michael Hanselmann
        out.write(line)
1506 edcd876b Michael Hanselmann
1507 edcd876b Michael Hanselmann
      out.flush()
1508 a8083063 Iustin Pop
    finally:
1509 59f82e3f Michael Hanselmann
      out.close()
1510 edcd876b Michael Hanselmann
1511 edcd876b Michael Hanselmann
  WriteFile(file_name, fn=_WriteEtcHosts, mode=0644)
1512 a8083063 Iustin Pop
1513 a8083063 Iustin Pop
1514 d9c02ca6 Michael Hanselmann
def RemoveHostFromEtcHosts(hostname):
1515 d9c02ca6 Michael Hanselmann
  """Wrapper around RemoveEtcHostsEntry.
1516 d9c02ca6 Michael Hanselmann

1517 58885d79 Iustin Pop
  @type hostname: str
1518 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
1519 58885d79 Iustin Pop
      full and shot name will be removed from
1520 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1521 58885d79 Iustin Pop

1522 d9c02ca6 Michael Hanselmann
  """
1523 b705c7a6 Manuel Franceschini
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hostname)
1524 b705c7a6 Manuel Franceschini
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hostname.split(".")[0])
1525 d9c02ca6 Michael Hanselmann
1526 d9c02ca6 Michael Hanselmann
1527 1d466a4f Michael Hanselmann
def TimestampForFilename():
1528 1d466a4f Michael Hanselmann
  """Returns the current time formatted for filenames.
1529 1d466a4f Michael Hanselmann

1530 1d466a4f Michael Hanselmann
  The format doesn't contain colons as some shells and applications them as
1531 1d466a4f Michael Hanselmann
  separators.
1532 1d466a4f Michael Hanselmann

1533 1d466a4f Michael Hanselmann
  """
1534 1d466a4f Michael Hanselmann
  return time.strftime("%Y-%m-%d_%H_%M_%S")
1535 1d466a4f Michael Hanselmann
1536 1d466a4f Michael Hanselmann
1537 a8083063 Iustin Pop
def CreateBackup(file_name):
1538 a8083063 Iustin Pop
  """Creates a backup of a file.
1539 a8083063 Iustin Pop

1540 58885d79 Iustin Pop
  @type file_name: str
1541 58885d79 Iustin Pop
  @param file_name: file to be backed up
1542 58885d79 Iustin Pop
  @rtype: str
1543 58885d79 Iustin Pop
  @return: the path to the newly created backup
1544 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
1545 a8083063 Iustin Pop

1546 a8083063 Iustin Pop
  """
1547 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
1548 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
1549 3ecf6786 Iustin Pop
                                file_name)
1550 a8083063 Iustin Pop
1551 1d466a4f Michael Hanselmann
  prefix = ("%s.backup-%s." %
1552 1d466a4f Michael Hanselmann
            (os.path.basename(file_name), TimestampForFilename()))
1553 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
1554 081b1e69 Michael Hanselmann
1555 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
1556 081b1e69 Michael Hanselmann
  try:
1557 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
1558 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
1559 081b1e69 Michael Hanselmann
    try:
1560 1d466a4f Michael Hanselmann
      logging.debug("Backing up %s at %s", file_name, backup_name)
1561 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
1562 081b1e69 Michael Hanselmann
    finally:
1563 081b1e69 Michael Hanselmann
      fdst.close()
1564 081b1e69 Michael Hanselmann
  finally:
1565 081b1e69 Michael Hanselmann
    fsrc.close()
1566 081b1e69 Michael Hanselmann
1567 a8083063 Iustin Pop
  return backup_name
1568 a8083063 Iustin Pop
1569 a8083063 Iustin Pop
1570 a8083063 Iustin Pop
def ShellQuote(value):
1571 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
1572 3ecf6786 Iustin Pop

1573 58885d79 Iustin Pop
  @type value: str
1574 58885d79 Iustin Pop
  @param value: the argument to be quoted
1575 58885d79 Iustin Pop
  @rtype: str
1576 58885d79 Iustin Pop
  @return: the quoted value
1577 58885d79 Iustin Pop

1578 a8083063 Iustin Pop
  """
1579 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
1580 a8083063 Iustin Pop
    return value
1581 a8083063 Iustin Pop
  else:
1582 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
1583 a8083063 Iustin Pop
1584 a8083063 Iustin Pop
1585 a8083063 Iustin Pop
def ShellQuoteArgs(args):
1586 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
1587 58885d79 Iustin Pop

1588 58885d79 Iustin Pop
  @type args: list
1589 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1590 58885d79 Iustin Pop
  @rtype: str
1591 5bbd3f7f Michael Hanselmann
  @return: the quoted arguments concatenated with spaces
1592 a8083063 Iustin Pop

1593 a8083063 Iustin Pop
  """
1594 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
1595 88d14415 Michael Hanselmann
1596 88d14415 Michael Hanselmann
1597 858905fb Michael Hanselmann
class ShellWriter:
1598 858905fb Michael Hanselmann
  """Helper class to write scripts with indentation.
1599 858905fb Michael Hanselmann

1600 858905fb Michael Hanselmann
  """
1601 858905fb Michael Hanselmann
  INDENT_STR = "  "
1602 858905fb Michael Hanselmann
1603 858905fb Michael Hanselmann
  def __init__(self, fh):
1604 858905fb Michael Hanselmann
    """Initializes this class.
1605 858905fb Michael Hanselmann

1606 858905fb Michael Hanselmann
    """
1607 858905fb Michael Hanselmann
    self._fh = fh
1608 858905fb Michael Hanselmann
    self._indent = 0
1609 858905fb Michael Hanselmann
1610 858905fb Michael Hanselmann
  def IncIndent(self):
1611 858905fb Michael Hanselmann
    """Increase indentation level by 1.
1612 858905fb Michael Hanselmann

1613 858905fb Michael Hanselmann
    """
1614 858905fb Michael Hanselmann
    self._indent += 1
1615 858905fb Michael Hanselmann
1616 858905fb Michael Hanselmann
  def DecIndent(self):
1617 858905fb Michael Hanselmann
    """Decrease indentation level by 1.
1618 858905fb Michael Hanselmann

1619 858905fb Michael Hanselmann
    """
1620 858905fb Michael Hanselmann
    assert self._indent > 0
1621 858905fb Michael Hanselmann
    self._indent -= 1
1622 858905fb Michael Hanselmann
1623 858905fb Michael Hanselmann
  def Write(self, txt, *args):
1624 858905fb Michael Hanselmann
    """Write line to output file.
1625 858905fb Michael Hanselmann

1626 858905fb Michael Hanselmann
    """
1627 858905fb Michael Hanselmann
    assert self._indent >= 0
1628 858905fb Michael Hanselmann
1629 858905fb Michael Hanselmann
    self._fh.write(self._indent * self.INDENT_STR)
1630 858905fb Michael Hanselmann
1631 858905fb Michael Hanselmann
    if args:
1632 858905fb Michael Hanselmann
      self._fh.write(txt % args)
1633 858905fb Michael Hanselmann
    else:
1634 858905fb Michael Hanselmann
      self._fh.write(txt)
1635 858905fb Michael Hanselmann
1636 858905fb Michael Hanselmann
    self._fh.write("\n")
1637 858905fb Michael Hanselmann
1638 858905fb Michael Hanselmann
1639 b5b8309d Guido Trotter
def ListVisibleFiles(path):
1640 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1641 58885d79 Iustin Pop

1642 58885d79 Iustin Pop
  @type path: str
1643 58885d79 Iustin Pop
  @param path: the directory to enumerate
1644 58885d79 Iustin Pop
  @rtype: list
1645 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1646 04a69a18 Iustin Pop
  @raise ProgrammerError: if L{path} is not an absolue and normalized path
1647 eedbda4b Michael Hanselmann

1648 eedbda4b Michael Hanselmann
  """
1649 04a69a18 Iustin Pop
  if not IsNormAbsPath(path):
1650 04a69a18 Iustin Pop
    raise errors.ProgrammerError("Path passed to ListVisibleFiles is not"
1651 04a69a18 Iustin Pop
                                 " absolute/normalized: '%s'" % path)
1652 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1653 f3299a07 Michael Hanselmann
  return files
1654 2f8b60b3 Iustin Pop
1655 2f8b60b3 Iustin Pop
1656 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1657 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1658 257f4c0a Iustin Pop

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

1663 2f8b60b3 Iustin Pop
  """
1664 2f8b60b3 Iustin Pop
  try:
1665 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1666 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1667 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1668 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1669 257f4c0a Iustin Pop
    else:
1670 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1671 257f4c0a Iustin Pop
                                   type(user))
1672 2f8b60b3 Iustin Pop
  except KeyError:
1673 2f8b60b3 Iustin Pop
    return default
1674 2f8b60b3 Iustin Pop
  return result.pw_dir
1675 59072e7e Michael Hanselmann
1676 59072e7e Michael Hanselmann
1677 24818e8f Michael Hanselmann
def NewUUID():
1678 59072e7e Michael Hanselmann
  """Returns a random UUID.
1679 59072e7e Michael Hanselmann

1680 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1681 58885d79 Iustin Pop
      filesystem.
1682 58885d79 Iustin Pop
  @rtype: str
1683 58885d79 Iustin Pop

1684 59072e7e Michael Hanselmann
  """
1685 13998ef2 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1686 087b34fe Iustin Pop
1687 087b34fe Iustin Pop
1688 ec2c2bc4 Luca Bigliardi
def GenerateSecret(numbytes=20):
1689 33081d90 Iustin Pop
  """Generates a random secret.
1690 33081d90 Iustin Pop

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

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

1699 33081d90 Iustin Pop
  """
1700 ec2c2bc4 Luca Bigliardi
  return os.urandom(numbytes).encode('hex')
1701 33081d90 Iustin Pop
1702 33081d90 Iustin Pop
1703 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1704 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1705 9dae41ad Guido Trotter

1706 9dae41ad Guido Trotter
  @param dirs: list of tuples (dir_name, dir_mode)
1707 9dae41ad Guido Trotter
  @type dirs: list of (string, integer)
1708 9dae41ad Guido Trotter

1709 9dae41ad Guido Trotter
  """
1710 9dae41ad Guido Trotter
  for dir_name, dir_mode in dirs:
1711 9dae41ad Guido Trotter
    try:
1712 1b2c8f85 Iustin Pop
      os.mkdir(dir_name, dir_mode)
1713 9dae41ad Guido Trotter
    except EnvironmentError, err:
1714 9dae41ad Guido Trotter
      if err.errno != errno.EEXIST:
1715 9dae41ad Guido Trotter
        raise errors.GenericError("Cannot create needed directory"
1716 1b2c8f85 Iustin Pop
                                  " '%s': %s" % (dir_name, err))
1717 b73360e3 Balazs Lecz
    try:
1718 b73360e3 Balazs Lecz
      os.chmod(dir_name, dir_mode)
1719 b73360e3 Balazs Lecz
    except EnvironmentError, err:
1720 b73360e3 Balazs Lecz
      raise errors.GenericError("Cannot change directory permissions on"
1721 b73360e3 Balazs Lecz
                                " '%s': %s" % (dir_name, err))
1722 9dae41ad Guido Trotter
    if not os.path.isdir(dir_name):
1723 9dae41ad Guido Trotter
      raise errors.GenericError("%s is not a directory" % dir_name)
1724 9dae41ad Guido Trotter
1725 9dae41ad Guido Trotter
1726 582ed043 Guido Trotter
def ReadFile(file_name, size=-1):
1727 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1728 ca0aa6d0 Michael Hanselmann

1729 016308cb Iustin Pop
  @type size: int
1730 016308cb Iustin Pop
  @param size: Read at most size bytes (if negative, entire file)
1731 58885d79 Iustin Pop
  @rtype: str
1732 5bbd3f7f Michael Hanselmann
  @return: the (possibly partial) content of the file
1733 ca0aa6d0 Michael Hanselmann

1734 ca0aa6d0 Michael Hanselmann
  """
1735 ca0aa6d0 Michael Hanselmann
  f = open(file_name, "r")
1736 ca0aa6d0 Michael Hanselmann
  try:
1737 582ed043 Guido Trotter
    return f.read(size)
1738 ca0aa6d0 Michael Hanselmann
  finally:
1739 ca0aa6d0 Michael Hanselmann
    f.close()
1740 ca0aa6d0 Michael Hanselmann
1741 ca0aa6d0 Michael Hanselmann
1742 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
1743 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
1744 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
1745 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
1746 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
1747 087b34fe Iustin Pop
  """(Over)write a file atomically.
1748 087b34fe Iustin Pop

1749 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
1750 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
1751 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
1752 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
1753 087b34fe Iustin Pop
  mtime/atime of the file.
1754 087b34fe Iustin Pop

1755 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1756 69efe319 Michael Hanselmann
  target file has the new contents. If the function has raised an
1757 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1758 087b34fe Iustin Pop
  temporary file should be removed.
1759 087b34fe Iustin Pop

1760 58885d79 Iustin Pop
  @type file_name: str
1761 58885d79 Iustin Pop
  @param file_name: the target filename
1762 58885d79 Iustin Pop
  @type fn: callable
1763 58885d79 Iustin Pop
  @param fn: content writing function, called with
1764 58885d79 Iustin Pop
      file descriptor as parameter
1765 69efe319 Michael Hanselmann
  @type data: str
1766 58885d79 Iustin Pop
  @param data: contents of the file
1767 58885d79 Iustin Pop
  @type mode: int
1768 58885d79 Iustin Pop
  @param mode: file mode
1769 58885d79 Iustin Pop
  @type uid: int
1770 58885d79 Iustin Pop
  @param uid: the owner of the file
1771 58885d79 Iustin Pop
  @type gid: int
1772 58885d79 Iustin Pop
  @param gid: the group of the file
1773 58885d79 Iustin Pop
  @type atime: int
1774 58885d79 Iustin Pop
  @param atime: a custom access time to be set on the file
1775 58885d79 Iustin Pop
  @type mtime: int
1776 58885d79 Iustin Pop
  @param mtime: a custom modification time to be set on the file
1777 58885d79 Iustin Pop
  @type close: boolean
1778 58885d79 Iustin Pop
  @param close: whether to close file after writing it
1779 58885d79 Iustin Pop
  @type prewrite: callable
1780 58885d79 Iustin Pop
  @param prewrite: function to be called before writing content
1781 58885d79 Iustin Pop
  @type postwrite: callable
1782 58885d79 Iustin Pop
  @param postwrite: function to be called after writing content
1783 58885d79 Iustin Pop

1784 58885d79 Iustin Pop
  @rtype: None or int
1785 58885d79 Iustin Pop
  @return: None if the 'close' parameter evaluates to True,
1786 58885d79 Iustin Pop
      otherwise the file descriptor
1787 58885d79 Iustin Pop

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

1790 087b34fe Iustin Pop
  """
1791 04a8d789 Michael Hanselmann
  if not os.path.isabs(file_name):
1792 087b34fe Iustin Pop
    raise errors.ProgrammerError("Path passed to WriteFile is not"
1793 087b34fe Iustin Pop
                                 " absolute: '%s'" % file_name)
1794 087b34fe Iustin Pop
1795 087b34fe Iustin Pop
  if [fn, data].count(None) != 1:
1796 087b34fe Iustin Pop
    raise errors.ProgrammerError("fn or data required")
1797 087b34fe Iustin Pop
1798 087b34fe Iustin Pop
  if [atime, mtime].count(None) == 1:
1799 087b34fe Iustin Pop
    raise errors.ProgrammerError("Both atime and mtime must be either"
1800 087b34fe Iustin Pop
                                 " set or None")
1801 087b34fe Iustin Pop
1802 70f4497c Michael Hanselmann
  if backup and not dry_run and os.path.isfile(file_name):
1803 70f4497c Michael Hanselmann
    CreateBackup(file_name)
1804 087b34fe Iustin Pop
1805 087b34fe Iustin Pop
  dir_name, base_name = os.path.split(file_name)
1806 087b34fe Iustin Pop
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
1807 81b7354c Iustin Pop
  do_remove = True
1808 087b34fe Iustin Pop
  # here we need to make sure we remove the temp file, if any error
1809 087b34fe Iustin Pop
  # leaves it in place
1810 087b34fe Iustin Pop
  try:
1811 087b34fe Iustin Pop
    if uid != -1 or gid != -1:
1812 087b34fe Iustin Pop
      os.chown(new_name, uid, gid)
1813 087b34fe Iustin Pop
    if mode:
1814 087b34fe Iustin Pop
      os.chmod(new_name, mode)
1815 71714516 Michael Hanselmann
    if callable(prewrite):
1816 71714516 Michael Hanselmann
      prewrite(fd)
1817 087b34fe Iustin Pop
    if data is not None:
1818 087b34fe Iustin Pop
      os.write(fd, data)
1819 087b34fe Iustin Pop
    else:
1820 087b34fe Iustin Pop
      fn(fd)
1821 71714516 Michael Hanselmann
    if callable(postwrite):
1822 71714516 Michael Hanselmann
      postwrite(fd)
1823 087b34fe Iustin Pop
    os.fsync(fd)
1824 087b34fe Iustin Pop
    if atime is not None and mtime is not None:
1825 087b34fe Iustin Pop
      os.utime(new_name, (atime, mtime))
1826 70f4497c Michael Hanselmann
    if not dry_run:
1827 70f4497c Michael Hanselmann
      os.rename(new_name, file_name)
1828 81b7354c Iustin Pop
      do_remove = False
1829 087b34fe Iustin Pop
  finally:
1830 71714516 Michael Hanselmann
    if close:
1831 71714516 Michael Hanselmann
      os.close(fd)
1832 71714516 Michael Hanselmann
      result = None
1833 71714516 Michael Hanselmann
    else:
1834 71714516 Michael Hanselmann
      result = fd
1835 81b7354c Iustin Pop
    if do_remove:
1836 81b7354c Iustin Pop
      RemoveFile(new_name)
1837 78feb6fb Guido Trotter
1838 71714516 Michael Hanselmann
  return result
1839 71714516 Michael Hanselmann
1840 78feb6fb Guido Trotter
1841 e587b46a Guido Trotter
def ReadOneLineFile(file_name, strict=False):
1842 e587b46a Guido Trotter
  """Return the first non-empty line from a file.
1843 e587b46a Guido Trotter

1844 e587b46a Guido Trotter
  @type strict: boolean
1845 e587b46a Guido Trotter
  @param strict: if True, abort if the file has more than one
1846 e587b46a Guido Trotter
      non-empty line
1847 e587b46a Guido Trotter

1848 e587b46a Guido Trotter
  """
1849 e587b46a Guido Trotter
  file_lines = ReadFile(file_name).splitlines()
1850 e587b46a Guido Trotter
  full_lines = filter(bool, file_lines)
1851 e587b46a Guido Trotter
  if not file_lines or not full_lines:
1852 e587b46a Guido Trotter
    raise errors.GenericError("No data in one-liner file %s" % file_name)
1853 e587b46a Guido Trotter
  elif strict and len(full_lines) > 1:
1854 e587b46a Guido Trotter
    raise errors.GenericError("Too many lines in one-liner file %s" %
1855 e587b46a Guido Trotter
                              file_name)
1856 e587b46a Guido Trotter
  return full_lines[0]
1857 e587b46a Guido Trotter
1858 e587b46a Guido Trotter
1859 7b4126b7 Iustin Pop
def FirstFree(seq, base=0):
1860 7b4126b7 Iustin Pop
  """Returns the first non-existing integer from seq.
1861 7b4126b7 Iustin Pop

1862 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
1863 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
1864 7b4126b7 Iustin Pop
  value, the index will be returned.
1865 7b4126b7 Iustin Pop

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

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

1871 58885d79 Iustin Pop
  @type seq: sequence
1872 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
1873 58885d79 Iustin Pop
  @type base: int
1874 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
1875 58885d79 Iustin Pop
  @rtype: int
1876 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
1877 7b4126b7 Iustin Pop

1878 7b4126b7 Iustin Pop
  """
1879 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
1880 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
1881 7b4126b7 Iustin Pop
    if elem > idx + base:
1882 7b4126b7 Iustin Pop
      # idx is not used
1883 7b4126b7 Iustin Pop
      return idx + base
1884 7b4126b7 Iustin Pop
  return None
1885 7b4126b7 Iustin Pop
1886 7b4126b7 Iustin Pop
1887 dfdc4060 Guido Trotter
def SingleWaitForFdCondition(fdobj, event, timeout):
1888 dcd511c8 Guido Trotter
  """Waits for a condition to occur on the socket.
1889 dcd511c8 Guido Trotter

1890 dfdc4060 Guido Trotter
  Immediately returns at the first interruption.
1891 dfdc4060 Guido Trotter

1892 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
1893 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
1894 dfdc4060 Guido Trotter
  @type event: integer
1895 dcd511c8 Guido Trotter
  @param event: ORed condition (see select module)
1896 dcd511c8 Guido Trotter
  @type timeout: float or None
1897 dcd511c8 Guido Trotter
  @param timeout: Timeout in seconds
1898 dcd511c8 Guido Trotter
  @rtype: int or None
1899 dcd511c8 Guido Trotter
  @return: None for timeout, otherwise occured conditions
1900 dcd511c8 Guido Trotter

1901 dcd511c8 Guido Trotter
  """
1902 dcd511c8 Guido Trotter
  check = (event | select.POLLPRI |
1903 dcd511c8 Guido Trotter
           select.POLLNVAL | select.POLLHUP | select.POLLERR)
1904 dcd511c8 Guido Trotter
1905 dcd511c8 Guido Trotter
  if timeout is not None:
1906 dcd511c8 Guido Trotter
    # Poller object expects milliseconds
1907 dcd511c8 Guido Trotter
    timeout *= 1000
1908 dcd511c8 Guido Trotter
1909 dcd511c8 Guido Trotter
  poller = select.poll()
1910 dfdc4060 Guido Trotter
  poller.register(fdobj, event)
1911 dcd511c8 Guido Trotter
  try:
1912 dfdc4060 Guido Trotter
    # TODO: If the main thread receives a signal and we have no timeout, we
1913 dfdc4060 Guido Trotter
    # could wait forever. This should check a global "quit" flag or something
1914 dfdc4060 Guido Trotter
    # every so often.
1915 dfdc4060 Guido Trotter
    io_events = poller.poll(timeout)
1916 dfdc4060 Guido Trotter
  except select.error, err:
1917 dfdc4060 Guido Trotter
    if err[0] != errno.EINTR:
1918 dfdc4060 Guido Trotter
      raise
1919 dfdc4060 Guido Trotter
    io_events = []
1920 dfdc4060 Guido Trotter
  if io_events and io_events[0][1] & check:
1921 dfdc4060 Guido Trotter
    return io_events[0][1]
1922 dfdc4060 Guido Trotter
  else:
1923 dfdc4060 Guido Trotter
    return None
1924 dfdc4060 Guido Trotter
1925 dfdc4060 Guido Trotter
1926 dfdc4060 Guido Trotter
class FdConditionWaiterHelper(object):
1927 dfdc4060 Guido Trotter
  """Retry helper for WaitForFdCondition.
1928 dfdc4060 Guido Trotter

1929 dfdc4060 Guido Trotter
  This class contains the retried and wait functions that make sure
1930 dfdc4060 Guido Trotter
  WaitForFdCondition can continue waiting until the timeout is actually
1931 dfdc4060 Guido Trotter
  expired.
1932 dfdc4060 Guido Trotter

1933 dfdc4060 Guido Trotter
  """
1934 dfdc4060 Guido Trotter
1935 dfdc4060 Guido Trotter
  def __init__(self, timeout):
1936 dfdc4060 Guido Trotter
    self.timeout = timeout
1937 dfdc4060 Guido Trotter
1938 dfdc4060 Guido Trotter
  def Poll(self, fdobj, event):
1939 dfdc4060 Guido Trotter
    result = SingleWaitForFdCondition(fdobj, event, self.timeout)
1940 dfdc4060 Guido Trotter
    if result is None:
1941 dfdc4060 Guido Trotter
      raise RetryAgain()
1942 dfdc4060 Guido Trotter
    else:
1943 dfdc4060 Guido Trotter
      return result
1944 dfdc4060 Guido Trotter
1945 dfdc4060 Guido Trotter
  def UpdateTimeout(self, timeout):
1946 dfdc4060 Guido Trotter
    self.timeout = timeout
1947 dfdc4060 Guido Trotter
1948 dfdc4060 Guido Trotter
1949 dfdc4060 Guido Trotter
def WaitForFdCondition(fdobj, event, timeout):
1950 dfdc4060 Guido Trotter
  """Waits for a condition to occur on the socket.
1951 dfdc4060 Guido Trotter

1952 dfdc4060 Guido Trotter
  Retries until the timeout is expired, even if interrupted.
1953 dfdc4060 Guido Trotter

1954 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
1955 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
1956 dfdc4060 Guido Trotter
  @type event: integer
1957 dfdc4060 Guido Trotter
  @param event: ORed condition (see select module)
1958 dfdc4060 Guido Trotter
  @type timeout: float or None
1959 dfdc4060 Guido Trotter
  @param timeout: Timeout in seconds
1960 dfdc4060 Guido Trotter
  @rtype: int or None
1961 dfdc4060 Guido Trotter
  @return: None for timeout, otherwise occured conditions
1962 dfdc4060 Guido Trotter

1963 dfdc4060 Guido Trotter
  """
1964 dfdc4060 Guido Trotter
  if timeout is not None:
1965 dfdc4060 Guido Trotter
    retrywaiter = FdConditionWaiterHelper(timeout)
1966 1b429e2a Iustin Pop
    try:
1967 1b429e2a Iustin Pop
      result = Retry(retrywaiter.Poll, RETRY_REMAINING_TIME, timeout,
1968 1b429e2a Iustin Pop
                     args=(fdobj, event), wait_fn=retrywaiter.UpdateTimeout)
1969 1b429e2a Iustin Pop
    except RetryTimeout:
1970 1b429e2a Iustin Pop
      result = None
1971 dfdc4060 Guido Trotter
  else:
1972 dfdc4060 Guido Trotter
    result = None
1973 dfdc4060 Guido Trotter
    while result is None:
1974 dfdc4060 Guido Trotter
      result = SingleWaitForFdCondition(fdobj, event, timeout)
1975 dfdc4060 Guido Trotter
  return result
1976 2de64672 Iustin Pop
1977 2de64672 Iustin Pop
1978 f7414041 Michael Hanselmann
def UniqueSequence(seq):
1979 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
1980 f7414041 Michael Hanselmann

1981 f7414041 Michael Hanselmann
  Element order is preserved.
1982 58885d79 Iustin Pop

1983 58885d79 Iustin Pop
  @type seq: sequence
1984 5bbd3f7f Michael Hanselmann
  @param seq: the sequence with the source elements
1985 58885d79 Iustin Pop
  @rtype: list
1986 58885d79 Iustin Pop
  @return: list of unique elements from seq
1987 58885d79 Iustin Pop

1988 f7414041 Michael Hanselmann
  """
1989 f7414041 Michael Hanselmann
  seen = set()
1990 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
1991 1862d460 Alexander Schreiber
1992 1862d460 Alexander Schreiber
1993 82187135 Renรฉ Nussbaumer
def NormalizeAndValidateMac(mac):
1994 82187135 Renรฉ Nussbaumer
  """Normalizes and check if a MAC address is valid.
1995 1862d460 Alexander Schreiber

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

1999 58885d79 Iustin Pop
  @type mac: str
2000 58885d79 Iustin Pop
  @param mac: the MAC to be validated
2001 82187135 Renรฉ Nussbaumer
  @rtype: str
2002 82187135 Renรฉ Nussbaumer
  @return: returns the normalized and validated MAC.
2003 82187135 Renรฉ Nussbaumer

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

2006 1862d460 Alexander Schreiber
  """
2007 82187135 Renรฉ Nussbaumer
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$", re.I)
2008 82187135 Renรฉ Nussbaumer
  if not mac_check.match(mac):
2009 82187135 Renรฉ Nussbaumer
    raise errors.OpPrereqError("Invalid MAC address specified: %s" %
2010 82187135 Renรฉ Nussbaumer
                               mac, errors.ECODE_INVAL)
2011 82187135 Renรฉ Nussbaumer
2012 82187135 Renรฉ Nussbaumer
  return mac.lower()
2013 06009e27 Iustin Pop
2014 06009e27 Iustin Pop
2015 06009e27 Iustin Pop
def TestDelay(duration):
2016 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
2017 06009e27 Iustin Pop

2018 58885d79 Iustin Pop
  @type duration: float
2019 58885d79 Iustin Pop
  @param duration: the sleep duration
2020 58885d79 Iustin Pop
  @rtype: boolean
2021 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
2022 58885d79 Iustin Pop

2023 06009e27 Iustin Pop
  """
2024 06009e27 Iustin Pop
  if duration < 0:
2025 38ea42a1 Iustin Pop
    return False, "Invalid sleep duration"
2026 06009e27 Iustin Pop
  time.sleep(duration)
2027 38ea42a1 Iustin Pop
  return True, None
2028 8f765069 Iustin Pop
2029 8f765069 Iustin Pop
2030 7d88772a Iustin Pop
def _CloseFDNoErr(fd, retries=5):
2031 7d88772a Iustin Pop
  """Close a file descriptor ignoring errors.
2032 8f765069 Iustin Pop

2033 7d88772a Iustin Pop
  @type fd: int
2034 7d88772a Iustin Pop
  @param fd: the file descriptor
2035 7d88772a Iustin Pop
  @type retries: int
2036 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
2037 7d88772a Iustin Pop
      other error than EBADF
2038 7d88772a Iustin Pop

2039 7d88772a Iustin Pop
  """
2040 7d88772a Iustin Pop
  try:
2041 7d88772a Iustin Pop
    os.close(fd)
2042 7d88772a Iustin Pop
  except OSError, err:
2043 7d88772a Iustin Pop
    if err.errno != errno.EBADF:
2044 7d88772a Iustin Pop
      if retries > 0:
2045 7d88772a Iustin Pop
        _CloseFDNoErr(fd, retries - 1)
2046 7d88772a Iustin Pop
    # else either it's closed already or we're out of retries, so we
2047 7d88772a Iustin Pop
    # ignore this and go on
2048 7d88772a Iustin Pop
2049 7d88772a Iustin Pop
2050 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
2051 7d88772a Iustin Pop
  """Close file descriptors.
2052 7d88772a Iustin Pop

2053 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
2054 7d88772a Iustin Pop
  stdin/out/err).
2055 8f765069 Iustin Pop

2056 58885d79 Iustin Pop
  @type noclose_fds: list or None
2057 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
2058 58885d79 Iustin Pop
      that should not be closed
2059 58885d79 Iustin Pop

2060 8f765069 Iustin Pop
  """
2061 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
2062 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
2063 8f765069 Iustin Pop
    try:
2064 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
2065 8f765069 Iustin Pop
      if MAXFD < 0:
2066 8f765069 Iustin Pop
        MAXFD = 1024
2067 8f765069 Iustin Pop
    except OSError:
2068 8f765069 Iustin Pop
      MAXFD = 1024
2069 8f765069 Iustin Pop
  else:
2070 8f765069 Iustin Pop
    MAXFD = 1024
2071 7d88772a Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
2072 7d88772a Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
2073 7d88772a Iustin Pop
    maxfd = MAXFD
2074 7d88772a Iustin Pop
2075 7d88772a Iustin Pop
  # Iterate through and close all file descriptors (except the standard ones)
2076 7d88772a Iustin Pop
  for fd in range(3, maxfd):
2077 7d88772a Iustin Pop
    if noclose_fds and fd in noclose_fds:
2078 7d88772a Iustin Pop
      continue
2079 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
2080 7d88772a Iustin Pop
2081 7d88772a Iustin Pop
2082 4c32a8bd Luca Bigliardi
def Mlockall(_ctypes=ctypes):
2083 4b6fa0bf Luca Bigliardi
  """Lock current process' virtual address space into RAM.
2084 4b6fa0bf Luca Bigliardi

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

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

2090 4b6fa0bf Luca Bigliardi
  """
2091 4c32a8bd Luca Bigliardi
  if _ctypes is None:
2092 4c32a8bd Luca Bigliardi
    raise errors.NoCtypesError()
2093 4b6fa0bf Luca Bigliardi
2094 4c32a8bd Luca Bigliardi
  libc = _ctypes.cdll.LoadLibrary("libc.so.6")
2095 4b6fa0bf Luca Bigliardi
  if libc is None:
2096 4b6fa0bf Luca Bigliardi
    logging.error("Cannot set memory lock, ctypes cannot load libc")
2097 4b6fa0bf Luca Bigliardi
    return
2098 4b6fa0bf Luca Bigliardi
2099 4b6fa0bf Luca Bigliardi
  # Some older version of the ctypes module don't have built-in functionality
2100 4b6fa0bf Luca Bigliardi
  # to access the errno global variable, where function error codes are stored.
2101 4b6fa0bf Luca Bigliardi
  # By declaring this variable as a pointer to an integer we can then access
2102 4b6fa0bf Luca Bigliardi
  # its value correctly, should the mlockall call fail, in order to see what
2103 4b6fa0bf Luca Bigliardi
  # the actual error code was.
2104 20601361 Luca Bigliardi
  # pylint: disable-msg=W0212
2105 4c32a8bd Luca Bigliardi
  libc.__errno_location.restype = _ctypes.POINTER(_ctypes.c_int)
2106 4b6fa0bf Luca Bigliardi
2107 4b6fa0bf Luca Bigliardi
  if libc.mlockall(_MCL_CURRENT | _MCL_FUTURE):
2108 20601361 Luca Bigliardi
    # pylint: disable-msg=W0212
2109 6ed0bbce Luca Bigliardi
    logging.error("Cannot set memory lock: %s",
2110 4b6fa0bf Luca Bigliardi
                  os.strerror(libc.__errno_location().contents.value))
2111 4b6fa0bf Luca Bigliardi
    return
2112 4b6fa0bf Luca Bigliardi
2113 4b6fa0bf Luca Bigliardi
  logging.debug("Memory lock set")
2114 4b6fa0bf Luca Bigliardi
2115 4b6fa0bf Luca Bigliardi
2116 0070a462 Renรฉ Nussbaumer
def Daemonize(logfile):
2117 7d88772a Iustin Pop
  """Daemonize the current process.
2118 7d88772a Iustin Pop

2119 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
2120 7d88772a Iustin Pop
  runs it in the background as a daemon.
2121 7d88772a Iustin Pop

2122 7d88772a Iustin Pop
  @type logfile: str
2123 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
2124 7d88772a Iustin Pop
  @rtype: int
2125 5fcc718f Iustin Pop
  @return: the value zero
2126 7d88772a Iustin Pop

2127 7d88772a Iustin Pop
  """
2128 7260cfbe Iustin Pop
  # pylint: disable-msg=W0212
2129 7260cfbe Iustin Pop
  # yes, we really want os._exit
2130 7d88772a Iustin Pop
  UMASK = 077
2131 7d88772a Iustin Pop
  WORKDIR = "/"
2132 8f765069 Iustin Pop
2133 8f765069 Iustin Pop
  # this might fail
2134 8f765069 Iustin Pop
  pid = os.fork()
2135 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
2136 8f765069 Iustin Pop
    os.setsid()
2137 8f765069 Iustin Pop
    # this might fail
2138 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
2139 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
2140 8f765069 Iustin Pop
      os.chdir(WORKDIR)
2141 8f765069 Iustin Pop
      os.umask(UMASK)
2142 8f765069 Iustin Pop
    else:
2143 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
2144 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
2145 8f765069 Iustin Pop
  else:
2146 8f765069 Iustin Pop
    os._exit(0) # Exit parent of the first child.
2147 8f765069 Iustin Pop
2148 7d88772a Iustin Pop
  for fd in range(3):
2149 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
2150 7d88772a Iustin Pop
  i = os.open("/dev/null", os.O_RDONLY) # stdin
2151 7d88772a Iustin Pop
  assert i == 0, "Can't close/reopen stdin"
2152 7d88772a Iustin Pop
  i = os.open(logfile, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0600) # stdout
2153 7d88772a Iustin Pop
  assert i == 1, "Can't close/reopen stdout"
2154 7d88772a Iustin Pop
  # Duplicate standard output to standard error.
2155 7d88772a Iustin Pop
  os.dup2(1, 2)
2156 8f765069 Iustin Pop
  return 0
2157 57c177af Iustin Pop
2158 57c177af Iustin Pop
2159 53beffbb Iustin Pop
def DaemonPidFileName(name):
2160 58885d79 Iustin Pop
  """Compute a ganeti pid file absolute path
2161 58885d79 Iustin Pop

2162 58885d79 Iustin Pop
  @type name: str
2163 58885d79 Iustin Pop
  @param name: the daemon name
2164 58885d79 Iustin Pop
  @rtype: str
2165 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
2166 58885d79 Iustin Pop
      daemon name
2167 b330ac0b Guido Trotter

2168 b330ac0b Guido Trotter
  """
2169 c4feafe8 Iustin Pop
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
2170 b330ac0b Guido Trotter
2171 b330ac0b Guido Trotter
2172 2826b361 Guido Trotter
def EnsureDaemon(name):
2173 2826b361 Guido Trotter
  """Check for and start daemon if not alive.
2174 2826b361 Guido Trotter

2175 2826b361 Guido Trotter
  """
2176 2826b361 Guido Trotter
  result = RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
2177 2826b361 Guido Trotter
  if result.failed:
2178 2826b361 Guido Trotter
    logging.error("Can't start daemon '%s', failure %s, output: %s",
2179 2826b361 Guido Trotter
                  name, result.fail_reason, result.output)
2180 2826b361 Guido Trotter
    return False
2181 2826b361 Guido Trotter
2182 2826b361 Guido Trotter
  return True
2183 b330ac0b Guido Trotter
2184 b330ac0b Guido Trotter
2185 db147305 Tom Limoncelli
def StopDaemon(name):
2186 db147305 Tom Limoncelli
  """Stop daemon
2187 db147305 Tom Limoncelli

2188 db147305 Tom Limoncelli
  """
2189 db147305 Tom Limoncelli
  result = RunCmd([constants.DAEMON_UTIL, "stop", name])
2190 db147305 Tom Limoncelli
  if result.failed:
2191 db147305 Tom Limoncelli
    logging.error("Can't stop daemon '%s', failure %s, output: %s",
2192 db147305 Tom Limoncelli
                  name, result.fail_reason, result.output)
2193 db147305 Tom Limoncelli
    return False
2194 db147305 Tom Limoncelli
2195 db147305 Tom Limoncelli
  return True
2196 db147305 Tom Limoncelli
2197 db147305 Tom Limoncelli
2198 b330ac0b Guido Trotter
def WritePidFile(name):
2199 b330ac0b Guido Trotter
  """Write the current process pidfile.
2200 b330ac0b Guido Trotter

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

2203 58885d79 Iustin Pop
  @type name: str
2204 58885d79 Iustin Pop
  @param name: the daemon name to use
2205 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
2206 58885d79 Iustin Pop
      points to a live process
2207 b330ac0b Guido Trotter

2208 b330ac0b Guido Trotter
  """
2209 b330ac0b Guido Trotter
  pid = os.getpid()
2210 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
2211 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
2212 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
2213 b330ac0b Guido Trotter
2214 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
2215 b330ac0b Guido Trotter
2216 b330ac0b Guido Trotter
2217 b330ac0b Guido Trotter
def RemovePidFile(name):
2218 b330ac0b Guido Trotter
  """Remove the current process pidfile.
2219 b330ac0b Guido Trotter

2220 b330ac0b Guido Trotter
  Any errors are ignored.
2221 b330ac0b Guido Trotter

2222 58885d79 Iustin Pop
  @type name: str
2223 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
2224 58885d79 Iustin Pop

2225 b330ac0b Guido Trotter
  """
2226 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
2227 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
2228 b330ac0b Guido Trotter
  try:
2229 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
2230 7260cfbe Iustin Pop
  except: # pylint: disable-msg=W0702
2231 b330ac0b Guido Trotter
    pass
2232 b330ac0b Guido Trotter
2233 b330ac0b Guido Trotter
2234 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
2235 ff5251bc Iustin Pop
                waitpid=False):
2236 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
2237 b2a1f511 Iustin Pop

2238 b2a1f511 Iustin Pop
  @type pid: int
2239 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
2240 38206f3c Iustin Pop
  @type signal_: int
2241 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
2242 b2a1f511 Iustin Pop
  @type timeout: int
2243 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
2244 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
2245 b2a1f511 Iustin Pop
                  will be done
2246 ff5251bc Iustin Pop
  @type waitpid: boolean
2247 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
2248 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
2249 ff5251bc Iustin Pop
      would remain as zombie
2250 b2a1f511 Iustin Pop

2251 b2a1f511 Iustin Pop
  """
2252 ff5251bc Iustin Pop
  def _helper(pid, signal_, wait):
2253 ff5251bc Iustin Pop
    """Simple helper to encapsulate the kill/waitpid sequence"""
2254 560cbec1 Michael Hanselmann
    if IgnoreProcessNotFound(os.kill, pid, signal_) and wait:
2255 ff5251bc Iustin Pop
      try:
2256 ff5251bc Iustin Pop
        os.waitpid(pid, os.WNOHANG)
2257 ff5251bc Iustin Pop
      except OSError:
2258 ff5251bc Iustin Pop
        pass
2259 ff5251bc Iustin Pop
2260 b2a1f511 Iustin Pop
  if pid <= 0:
2261 b2a1f511 Iustin Pop
    # kill with pid=0 == suicide
2262 b2a1f511 Iustin Pop
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
2263 b2a1f511 Iustin Pop
2264 b2a1f511 Iustin Pop
  if not IsProcessAlive(pid):
2265 b2a1f511 Iustin Pop
    return
2266 31892b4c Michael Hanselmann
2267 ff5251bc Iustin Pop
  _helper(pid, signal_, waitpid)
2268 31892b4c Michael Hanselmann
2269 b2a1f511 Iustin Pop
  if timeout <= 0:
2270 b2a1f511 Iustin Pop
    return
2271 7167159a Michael Hanselmann
2272 31892b4c Michael Hanselmann
  def _CheckProcess():
2273 31892b4c Michael Hanselmann
    if not IsProcessAlive(pid):
2274 31892b4c Michael Hanselmann
      return
2275 31892b4c Michael Hanselmann
2276 7167159a Michael Hanselmann
    try:
2277 7167159a Michael Hanselmann
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
2278 7167159a Michael Hanselmann
    except OSError:
2279 31892b4c Michael Hanselmann
      raise RetryAgain()
2280 31892b4c Michael Hanselmann
2281 31892b4c Michael Hanselmann
    if result_pid > 0:
2282 31892b4c Michael Hanselmann
      return
2283 31892b4c Michael Hanselmann
2284 31892b4c Michael Hanselmann
    raise RetryAgain()
2285 31892b4c Michael Hanselmann
2286 31892b4c Michael Hanselmann
  try:
2287 31892b4c Michael Hanselmann
    # Wait up to $timeout seconds
2288 31892b4c Michael Hanselmann
    Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
2289 31892b4c Michael Hanselmann
  except RetryTimeout:
2290 31892b4c Michael Hanselmann
    pass
2291 7167159a Michael Hanselmann
2292 b2a1f511 Iustin Pop
  if IsProcessAlive(pid):
2293 7167159a Michael Hanselmann
    # Kill process if it's still alive
2294 e1bd0072 Iustin Pop
    _helper(pid, signal.SIGKILL, waitpid)
2295 b2a1f511 Iustin Pop
2296 b2a1f511 Iustin Pop
2297 57c177af Iustin Pop
def FindFile(name, search_path, test=os.path.exists):
2298 57c177af Iustin Pop
  """Look for a filesystem object in a given path.
2299 57c177af Iustin Pop

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

2303 58885d79 Iustin Pop
  @type name: str
2304 58885d79 Iustin Pop
  @param name: the name to look for
2305 58885d79 Iustin Pop
  @type search_path: str
2306 58885d79 Iustin Pop
  @param search_path: location to start at
2307 58885d79 Iustin Pop
  @type test: callable
2308 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
2309 58885d79 Iustin Pop
      if the a given object is valid; the default value is
2310 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
2311 58885d79 Iustin Pop
  @rtype: str or None
2312 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
2313 57c177af Iustin Pop

2314 57c177af Iustin Pop
  """
2315 f95c81bf Iustin Pop
  # validate the filename mask
2316 f95c81bf Iustin Pop
  if constants.EXT_PLUGIN_MASK.match(name) is None:
2317 f95c81bf Iustin Pop
    logging.critical("Invalid value passed for external script name: '%s'",
2318 f95c81bf Iustin Pop
                     name)
2319 f95c81bf Iustin Pop
    return None
2320 f95c81bf Iustin Pop
2321 57c177af Iustin Pop
  for dir_name in search_path:
2322 e02b9114 Iustin Pop
    # FIXME: investigate switch to PathJoin
2323 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
2324 f95c81bf Iustin Pop
    # check the user test and that we're indeed resolving to the given
2325 f95c81bf Iustin Pop
    # basename
2326 f95c81bf Iustin Pop
    if test(item_name) and os.path.basename(item_name) == name:
2327 57c177af Iustin Pop
      return item_name
2328 57c177af Iustin Pop
  return None
2329 8d1a2a64 Michael Hanselmann
2330 8d1a2a64 Michael Hanselmann
2331 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
2332 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
2333 8d1a2a64 Michael Hanselmann

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

2337 58885d79 Iustin Pop
  @type vglist: dict
2338 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
2339 58885d79 Iustin Pop
  @type vgname: str
2340 58885d79 Iustin Pop
  @param vgname: the volume group we should check
2341 58885d79 Iustin Pop
  @type minsize: int
2342 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
2343 58885d79 Iustin Pop
  @rtype: None or str
2344 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
2345 8d1a2a64 Michael Hanselmann

2346 8d1a2a64 Michael Hanselmann
  """
2347 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
2348 8d1a2a64 Michael Hanselmann
  if vgsize is None:
2349 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
2350 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
2351 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
2352 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
2353 8d1a2a64 Michael Hanselmann
  return None
2354 7996a135 Iustin Pop
2355 7996a135 Iustin Pop
2356 45bc5e4a Michael Hanselmann
def SplitTime(value):
2357 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
2358 739be818 Michael Hanselmann

2359 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
2360 45bc5e4a Michael Hanselmann
  @type value: int or float
2361 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
2362 739be818 Michael Hanselmann

2363 739be818 Michael Hanselmann
  """
2364 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
2365 45bc5e4a Michael Hanselmann
2366 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
2367 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
2368 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
2369 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
2370 45bc5e4a Michael Hanselmann
2371 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
2372 739be818 Michael Hanselmann
2373 739be818 Michael Hanselmann
2374 739be818 Michael Hanselmann
def MergeTime(timetuple):
2375 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
2376 739be818 Michael Hanselmann

2377 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
2378 739be818 Michael Hanselmann
  @type timetuple: tuple
2379 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
2380 739be818 Michael Hanselmann

2381 739be818 Michael Hanselmann
  """
2382 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
2383 739be818 Michael Hanselmann
2384 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
2385 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
2386 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
2387 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
2388 739be818 Michael Hanselmann
2389 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
2390 739be818 Michael Hanselmann
2391 739be818 Michael Hanselmann
2392 de3b8e39 Luca Bigliardi
class LogFileHandler(logging.FileHandler):
2393 de3b8e39 Luca Bigliardi
  """Log handler that doesn't fallback to stderr.
2394 de3b8e39 Luca Bigliardi

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

2399 de3b8e39 Luca Bigliardi
  """
2400 de3b8e39 Luca Bigliardi
  def __init__(self, filename, mode="a", encoding=None):
2401 de3b8e39 Luca Bigliardi
    """Open the specified file and use it as the stream for logging.
2402 de3b8e39 Luca Bigliardi

2403 de3b8e39 Luca Bigliardi
    Also open /dev/console to report errors while logging.
2404 de3b8e39 Luca Bigliardi

2405 de3b8e39 Luca Bigliardi
    """
2406 de3b8e39 Luca Bigliardi
    logging.FileHandler.__init__(self, filename, mode, encoding)
2407 de3b8e39 Luca Bigliardi
    self.console = open(constants.DEV_CONSOLE, "a")
2408 de3b8e39 Luca Bigliardi
2409 20601361 Luca Bigliardi
  def handleError(self, record): # pylint: disable-msg=C0103
2410 de3b8e39 Luca Bigliardi
    """Handle errors which occur during an emit() call.
2411 de3b8e39 Luca Bigliardi

2412 de3b8e39 Luca Bigliardi
    Try to handle errors with FileHandler method, if it fails write to
2413 de3b8e39 Luca Bigliardi
    /dev/console.
2414 de3b8e39 Luca Bigliardi

2415 de3b8e39 Luca Bigliardi
    """
2416 de3b8e39 Luca Bigliardi
    try:
2417 05b35f15 Luca Bigliardi
      logging.FileHandler.handleError(self, record)
2418 20601361 Luca Bigliardi
    except Exception: # pylint: disable-msg=W0703
2419 de3b8e39 Luca Bigliardi
      try:
2420 de3b8e39 Luca Bigliardi
        self.console.write("Cannot log message:\n%s\n" % self.format(record))
2421 20601361 Luca Bigliardi
      except Exception: # pylint: disable-msg=W0703
2422 de3b8e39 Luca Bigliardi
        # Log handler tried everything it could, now just give up
2423 de3b8e39 Luca Bigliardi
        pass
2424 de3b8e39 Luca Bigliardi
2425 de3b8e39 Luca Bigliardi
2426 ea34193f Iustin Pop
def SetupLogging(logfile, debug=0, stderr_logging=False, program="",
2427 49e60a28 Luca Bigliardi
                 multithreaded=False, syslog=constants.SYSLOG_USAGE,
2428 49e60a28 Luca Bigliardi
                 console_logging=False):
2429 82d9caef Iustin Pop
  """Configures the logging module.
2430 82d9caef Iustin Pop

2431 58885d79 Iustin Pop
  @type logfile: str
2432 58885d79 Iustin Pop
  @param logfile: the filename to which we should log
2433 ea34193f Iustin Pop
  @type debug: integer
2434 ea34193f Iustin Pop
  @param debug: if greater than zero, enable debug messages, otherwise
2435 58885d79 Iustin Pop
      only those at C{INFO} and above level
2436 58885d79 Iustin Pop
  @type stderr_logging: boolean
2437 58885d79 Iustin Pop
  @param stderr_logging: whether we should also log to the standard error
2438 58885d79 Iustin Pop
  @type program: str
2439 58885d79 Iustin Pop
  @param program: the name under which we should log messages
2440 d21d09d6 Iustin Pop
  @type multithreaded: boolean
2441 d21d09d6 Iustin Pop
  @param multithreaded: if True, will add the thread name to the log file
2442 551b6283 Iustin Pop
  @type syslog: string
2443 551b6283 Iustin Pop
  @param syslog: one of 'no', 'yes', 'only':
2444 551b6283 Iustin Pop
      - if no, syslog is not used
2445 551b6283 Iustin Pop
      - if yes, syslog is used (in addition to file-logging)
2446 551b6283 Iustin Pop
      - if only, only syslog is used
2447 49e60a28 Luca Bigliardi
  @type console_logging: boolean
2448 49e60a28 Luca Bigliardi
  @param console_logging: if True, will use a FileHandler which falls back to
2449 49e60a28 Luca Bigliardi
      the system console if logging fails
2450 58885d79 Iustin Pop
  @raise EnvironmentError: if we can't open the log file and
2451 551b6283 Iustin Pop
      syslog/stderr logging is disabled
2452 58885d79 Iustin Pop

2453 82d9caef Iustin Pop
  """
2454 d21d09d6 Iustin Pop
  fmt = "%(asctime)s: " + program + " pid=%(process)d"
2455 551b6283 Iustin Pop
  sft = program + "[%(process)d]:"
2456 d21d09d6 Iustin Pop
  if multithreaded:
2457 d21d09d6 Iustin Pop
    fmt += "/%(threadName)s"
2458 551b6283 Iustin Pop
    sft += " (%(threadName)s)"
2459 82d9caef Iustin Pop
  if debug:
2460 d21d09d6 Iustin Pop
    fmt += " %(module)s:%(lineno)s"
2461 551b6283 Iustin Pop
    # no debug info for syslog loggers
2462 d21d09d6 Iustin Pop
  fmt += " %(levelname)s %(message)s"
2463 551b6283 Iustin Pop
  # yes, we do want the textual level, as remote syslog will probably
2464 551b6283 Iustin Pop
  # lose the error level, and it's easier to grep for it
2465 551b6283 Iustin Pop
  sft += " %(levelname)s %(message)s"
2466 82d9caef Iustin Pop
  formatter = logging.Formatter(fmt)
2467 551b6283 Iustin Pop
  sys_fmt = logging.Formatter(sft)
2468 82d9caef Iustin Pop
2469 82d9caef Iustin Pop
  root_logger = logging.getLogger("")
2470 82d9caef Iustin Pop
  root_logger.setLevel(logging.NOTSET)
2471 82d9caef Iustin Pop
2472 6346a9e5 Michael Hanselmann
  # Remove all previously setup handlers
2473 6346a9e5 Michael Hanselmann
  for handler in root_logger.handlers:
2474 7d88772a Iustin Pop
    handler.close()
2475 6346a9e5 Michael Hanselmann
    root_logger.removeHandler(handler)
2476 6346a9e5 Michael Hanselmann
2477 82d9caef Iustin Pop
  if stderr_logging:
2478 82d9caef Iustin Pop
    stderr_handler = logging.StreamHandler()
2479 82d9caef Iustin Pop
    stderr_handler.setFormatter(formatter)
2480 82d9caef Iustin Pop
    if debug:
2481 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.NOTSET)
2482 82d9caef Iustin Pop
    else:
2483 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.CRITICAL)
2484 82d9caef Iustin Pop
    root_logger.addHandler(stderr_handler)
2485 82d9caef Iustin Pop
2486 551b6283 Iustin Pop
  if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY):
2487 551b6283 Iustin Pop
    facility = logging.handlers.SysLogHandler.LOG_DAEMON
2488 551b6283 Iustin Pop
    syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET,
2489 551b6283 Iustin Pop
                                                    facility)
2490 551b6283 Iustin Pop
    syslog_handler.setFormatter(sys_fmt)
2491 551b6283 Iustin Pop
    # Never enable debug over syslog
2492 551b6283 Iustin Pop
    syslog_handler.setLevel(logging.INFO)
2493 551b6283 Iustin Pop
    root_logger.addHandler(syslog_handler)
2494 551b6283 Iustin Pop
2495 551b6283 Iustin Pop
  if syslog != constants.SYSLOG_ONLY:
2496 551b6283 Iustin Pop
    # this can fail, if the logging directories are not setup or we have
2497 551b6283 Iustin Pop
    # a permisssion problem; in this case, it's best to log but ignore
2498 551b6283 Iustin Pop
    # the error if stderr_logging is True, and if false we re-raise the
2499 551b6283 Iustin Pop
    # exception since otherwise we could run but without any logs at all
2500 551b6283 Iustin Pop
    try:
2501 49e60a28 Luca Bigliardi
      if console_logging:
2502 49e60a28 Luca Bigliardi
        logfile_handler = LogFileHandler(logfile)
2503 49e60a28 Luca Bigliardi
      else:
2504 49e60a28 Luca Bigliardi
        logfile_handler = logging.FileHandler(logfile)
2505 551b6283 Iustin Pop
      logfile_handler.setFormatter(formatter)
2506 551b6283 Iustin Pop
      if debug:
2507 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.DEBUG)
2508 551b6283 Iustin Pop
      else:
2509 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.INFO)
2510 551b6283 Iustin Pop
      root_logger.addHandler(logfile_handler)
2511 551b6283 Iustin Pop
    except EnvironmentError:
2512 551b6283 Iustin Pop
      if stderr_logging or syslog == constants.SYSLOG_YES:
2513 551b6283 Iustin Pop
        logging.exception("Failed to enable logging to file '%s'", logfile)
2514 551b6283 Iustin Pop
      else:
2515 551b6283 Iustin Pop
        # we need to re-raise the exception
2516 551b6283 Iustin Pop
        raise
2517 82d9caef Iustin Pop
2518 016d04b3 Michael Hanselmann
2519 da961187 Guido Trotter
def IsNormAbsPath(path):
2520 17c61836 Guido Trotter
  """Check whether a path is absolute and also normalized
2521 da961187 Guido Trotter

2522 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
2523 da961187 Guido Trotter

2524 da961187 Guido Trotter
  """
2525 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
2526 82d9caef Iustin Pop
2527 016d04b3 Michael Hanselmann
2528 4bb678e9 Iustin Pop
def PathJoin(*args):
2529 4bb678e9 Iustin Pop
  """Safe-join a list of path components.
2530 4bb678e9 Iustin Pop

2531 4bb678e9 Iustin Pop
  Requirements:
2532 4bb678e9 Iustin Pop
      - the first argument must be an absolute path
2533 4bb678e9 Iustin Pop
      - no component in the path must have backtracking (e.g. /../),
2534 4bb678e9 Iustin Pop
        since we check for normalization at the end
2535 4bb678e9 Iustin Pop

2536 4bb678e9 Iustin Pop
  @param args: the path components to be joined
2537 4bb678e9 Iustin Pop
  @raise ValueError: for invalid paths
2538 4bb678e9 Iustin Pop

2539 4bb678e9 Iustin Pop
  """
2540 4bb678e9 Iustin Pop
  # ensure we're having at least one path passed in
2541 4bb678e9 Iustin Pop
  assert args
2542 4bb678e9 Iustin Pop
  # ensure the first component is an absolute and normalized path name
2543 4bb678e9 Iustin Pop
  root = args[0]
2544 4bb678e9 Iustin Pop
  if not IsNormAbsPath(root):
2545 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
2546 4bb678e9 Iustin Pop
  result = os.path.join(*args)
2547 4bb678e9 Iustin Pop
  # ensure that the whole path is normalized
2548 4bb678e9 Iustin Pop
  if not IsNormAbsPath(result):
2549 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
2550 4bb678e9 Iustin Pop
  # check that we're still under the original prefix
2551 4bb678e9 Iustin Pop
  prefix = os.path.commonprefix([root, result])
2552 4bb678e9 Iustin Pop
  if prefix != root:
2553 4bb678e9 Iustin Pop
    raise ValueError("Error: path joining resulted in different prefix"
2554 4bb678e9 Iustin Pop
                     " (%s != %s)" % (prefix, root))
2555 4bb678e9 Iustin Pop
  return result
2556 4bb678e9 Iustin Pop
2557 4bb678e9 Iustin Pop
2558 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
2559 f65f63ef Iustin Pop
  """Return the last lines from a file.
2560 f65f63ef Iustin Pop

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

2565 f65f63ef Iustin Pop
  @param fname: the file name
2566 f65f63ef Iustin Pop
  @type lines: int
2567 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
2568 f65f63ef Iustin Pop

2569 f65f63ef Iustin Pop
  """
2570 f65f63ef Iustin Pop
  fd = open(fname, "r")
2571 f65f63ef Iustin Pop
  try:
2572 f65f63ef Iustin Pop
    fd.seek(0, 2)
2573 f65f63ef Iustin Pop
    pos = fd.tell()
2574 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
2575 f65f63ef Iustin Pop
    fd.seek(pos, 0)
2576 f65f63ef Iustin Pop
    raw_data = fd.read()
2577 f65f63ef Iustin Pop
  finally:
2578 f65f63ef Iustin Pop
    fd.close()
2579 f65f63ef Iustin Pop
2580 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
2581 f65f63ef Iustin Pop
  return rows[-lines:]
2582 f65f63ef Iustin Pop
2583 f65f63ef Iustin Pop
2584 24d70417 Michael Hanselmann
def FormatTimestampWithTZ(secs):
2585 24d70417 Michael Hanselmann
  """Formats a Unix timestamp with the local timezone.
2586 24d70417 Michael Hanselmann

2587 24d70417 Michael Hanselmann
  """
2588 24d70417 Michael Hanselmann
  return time.strftime("%F %T %Z", time.gmtime(secs))
2589 24d70417 Michael Hanselmann
2590 24d70417 Michael Hanselmann
2591 27e46076 Michael Hanselmann
def _ParseAsn1Generalizedtime(value):
2592 27e46076 Michael Hanselmann
  """Parses an ASN1 GENERALIZEDTIME timestamp as used by pyOpenSSL.
2593 27e46076 Michael Hanselmann

2594 27e46076 Michael Hanselmann
  @type value: string
2595 27e46076 Michael Hanselmann
  @param value: ASN1 GENERALIZEDTIME timestamp
2596 27e46076 Michael Hanselmann

2597 27e46076 Michael Hanselmann
  """
2598 27e46076 Michael Hanselmann
  m = re.match(r"^(\d+)([-+]\d\d)(\d\d)$", value)
2599 27e46076 Michael Hanselmann
  if m:
2600 27e46076 Michael Hanselmann
    # We have an offset
2601 27e46076 Michael Hanselmann
    asn1time = m.group(1)
2602 27e46076 Michael Hanselmann
    hours = int(m.group(2))
2603 27e46076 Michael Hanselmann
    minutes = int(m.group(3))
2604 27e46076 Michael Hanselmann
    utcoffset = (60 * hours) + minutes
2605 27e46076 Michael Hanselmann
  else:
2606 27e46076 Michael Hanselmann
    if not value.endswith("Z"):
2607 27e46076 Michael Hanselmann
      raise ValueError("Missing timezone")
2608 27e46076 Michael Hanselmann
    asn1time = value[:-1]
2609 27e46076 Michael Hanselmann
    utcoffset = 0
2610 27e46076 Michael Hanselmann
2611 27e46076 Michael Hanselmann
  parsed = time.strptime(asn1time, "%Y%m%d%H%M%S")
2612 27e46076 Michael Hanselmann
2613 27e46076 Michael Hanselmann
  tt = datetime.datetime(*(parsed[:7])) - datetime.timedelta(minutes=utcoffset)
2614 27e46076 Michael Hanselmann
2615 27e46076 Michael Hanselmann
  return calendar.timegm(tt.utctimetuple())
2616 27e46076 Michael Hanselmann
2617 27e46076 Michael Hanselmann
2618 27e46076 Michael Hanselmann
def GetX509CertValidity(cert):
2619 27e46076 Michael Hanselmann
  """Returns the validity period of the certificate.
2620 27e46076 Michael Hanselmann

2621 27e46076 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2622 27e46076 Michael Hanselmann
  @param cert: X509 certificate object
2623 27e46076 Michael Hanselmann

2624 27e46076 Michael Hanselmann
  """
2625 27e46076 Michael Hanselmann
  # The get_notBefore and get_notAfter functions are only supported in
2626 27e46076 Michael Hanselmann
  # pyOpenSSL 0.7 and above.
2627 27e46076 Michael Hanselmann
  try:
2628 27e46076 Michael Hanselmann
    get_notbefore_fn = cert.get_notBefore
2629 27e46076 Michael Hanselmann
  except AttributeError:
2630 27e46076 Michael Hanselmann
    not_before = None
2631 27e46076 Michael Hanselmann
  else:
2632 27e46076 Michael Hanselmann
    not_before_asn1 = get_notbefore_fn()
2633 27e46076 Michael Hanselmann
2634 27e46076 Michael Hanselmann
    if not_before_asn1 is None:
2635 27e46076 Michael Hanselmann
      not_before = None
2636 27e46076 Michael Hanselmann
    else:
2637 27e46076 Michael Hanselmann
      not_before = _ParseAsn1Generalizedtime(not_before_asn1)
2638 27e46076 Michael Hanselmann
2639 27e46076 Michael Hanselmann
  try:
2640 27e46076 Michael Hanselmann
    get_notafter_fn = cert.get_notAfter
2641 27e46076 Michael Hanselmann
  except AttributeError:
2642 27e46076 Michael Hanselmann
    not_after = None
2643 27e46076 Michael Hanselmann
  else:
2644 27e46076 Michael Hanselmann
    not_after_asn1 = get_notafter_fn()
2645 27e46076 Michael Hanselmann
2646 27e46076 Michael Hanselmann
    if not_after_asn1 is None:
2647 27e46076 Michael Hanselmann
      not_after = None
2648 27e46076 Michael Hanselmann
    else:
2649 27e46076 Michael Hanselmann
      not_after = _ParseAsn1Generalizedtime(not_after_asn1)
2650 27e46076 Michael Hanselmann
2651 27e46076 Michael Hanselmann
  return (not_before, not_after)
2652 27e46076 Michael Hanselmann
2653 27e46076 Michael Hanselmann
2654 24d70417 Michael Hanselmann
def _VerifyCertificateInner(expired, not_before, not_after, now,
2655 24d70417 Michael Hanselmann
                            warn_days, error_days):
2656 24d70417 Michael Hanselmann
  """Verifies certificate validity.
2657 24d70417 Michael Hanselmann

2658 24d70417 Michael Hanselmann
  @type expired: bool
2659 24d70417 Michael Hanselmann
  @param expired: Whether pyOpenSSL considers the certificate as expired
2660 24d70417 Michael Hanselmann
  @type not_before: number or None
2661 24d70417 Michael Hanselmann
  @param not_before: Unix timestamp before which certificate is not valid
2662 24d70417 Michael Hanselmann
  @type not_after: number or None
2663 24d70417 Michael Hanselmann
  @param not_after: Unix timestamp after which certificate is invalid
2664 24d70417 Michael Hanselmann
  @type now: number
2665 24d70417 Michael Hanselmann
  @param now: Current time as Unix timestamp
2666 24d70417 Michael Hanselmann
  @type warn_days: number or None
2667 24d70417 Michael Hanselmann
  @param warn_days: How many days before expiration a warning should be reported
2668 24d70417 Michael Hanselmann
  @type error_days: number or None
2669 24d70417 Michael Hanselmann
  @param error_days: How many days before expiration an error should be reported
2670 24d70417 Michael Hanselmann

2671 24d70417 Michael Hanselmann
  """
2672 24d70417 Michael Hanselmann
  if expired:
2673 24d70417 Michael Hanselmann
    msg = "Certificate is expired"
2674 24d70417 Michael Hanselmann
2675 24d70417 Michael Hanselmann
    if not_before is not None and not_after is not None:
2676 24d70417 Michael Hanselmann
      msg += (" (valid from %s to %s)" %
2677 24d70417 Michael Hanselmann
              (FormatTimestampWithTZ(not_before),
2678 24d70417 Michael Hanselmann
               FormatTimestampWithTZ(not_after)))
2679 24d70417 Michael Hanselmann
    elif not_before is not None:
2680 24d70417 Michael Hanselmann
      msg += " (valid from %s)" % FormatTimestampWithTZ(not_before)
2681 24d70417 Michael Hanselmann
    elif not_after is not None:
2682 24d70417 Michael Hanselmann
      msg += " (valid until %s)" % FormatTimestampWithTZ(not_after)
2683 24d70417 Michael Hanselmann
2684 24d70417 Michael Hanselmann
    return (CERT_ERROR, msg)
2685 24d70417 Michael Hanselmann
2686 24d70417 Michael Hanselmann
  elif not_before is not None and not_before > now:
2687 24d70417 Michael Hanselmann
    return (CERT_WARNING,
2688 24d70417 Michael Hanselmann
            "Certificate not yet valid (valid from %s)" %
2689 24d70417 Michael Hanselmann
            FormatTimestampWithTZ(not_before))
2690 24d70417 Michael Hanselmann
2691 24d70417 Michael Hanselmann
  elif not_after is not None:
2692 24d70417 Michael Hanselmann
    remaining_days = int((not_after - now) / (24 * 3600))
2693 24d70417 Michael Hanselmann
2694 24d70417 Michael Hanselmann
    msg = "Certificate expires in about %d days" % remaining_days
2695 24d70417 Michael Hanselmann
2696 24d70417 Michael Hanselmann
    if error_days is not None and remaining_days <= error_days:
2697 24d70417 Michael Hanselmann
      return (CERT_ERROR, msg)
2698 24d70417 Michael Hanselmann
2699 24d70417 Michael Hanselmann
    if warn_days is not None and remaining_days <= warn_days:
2700 24d70417 Michael Hanselmann
      return (CERT_WARNING, msg)
2701 24d70417 Michael Hanselmann
2702 24d70417 Michael Hanselmann
  return (None, None)
2703 24d70417 Michael Hanselmann
2704 24d70417 Michael Hanselmann
2705 24d70417 Michael Hanselmann
def VerifyX509Certificate(cert, warn_days, error_days):
2706 24d70417 Michael Hanselmann
  """Verifies a certificate for LUVerifyCluster.
2707 24d70417 Michael Hanselmann

2708 24d70417 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2709 24d70417 Michael Hanselmann
  @param cert: X509 certificate object
2710 24d70417 Michael Hanselmann
  @type warn_days: number or None
2711 24d70417 Michael Hanselmann
  @param warn_days: How many days before expiration a warning should be reported
2712 24d70417 Michael Hanselmann
  @type error_days: number or None
2713 24d70417 Michael Hanselmann
  @param error_days: How many days before expiration an error should be reported
2714 24d70417 Michael Hanselmann

2715 24d70417 Michael Hanselmann
  """
2716 24d70417 Michael Hanselmann
  # Depending on the pyOpenSSL version, this can just return (None, None)
2717 24d70417 Michael Hanselmann
  (not_before, not_after) = GetX509CertValidity(cert)
2718 24d70417 Michael Hanselmann
2719 24d70417 Michael Hanselmann
  return _VerifyCertificateInner(cert.has_expired(), not_before, not_after,
2720 24d70417 Michael Hanselmann
                                 time.time(), warn_days, error_days)
2721 24d70417 Michael Hanselmann
2722 24d70417 Michael Hanselmann
2723 68857643 Michael Hanselmann
def SignX509Certificate(cert, key, salt):
2724 68857643 Michael Hanselmann
  """Sign a X509 certificate.
2725 68857643 Michael Hanselmann

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

2728 68857643 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2729 68857643 Michael Hanselmann
  @param cert: X509 certificate object
2730 68857643 Michael Hanselmann
  @type key: string
2731 68857643 Michael Hanselmann
  @param key: Key for HMAC
2732 68857643 Michael Hanselmann
  @type salt: string
2733 68857643 Michael Hanselmann
  @param salt: Salt for HMAC
2734 68857643 Michael Hanselmann
  @rtype: string
2735 68857643 Michael Hanselmann
  @return: Serialized and signed certificate in PEM format
2736 68857643 Michael Hanselmann

2737 68857643 Michael Hanselmann
  """
2738 68857643 Michael Hanselmann
  if not VALID_X509_SIGNATURE_SALT.match(salt):
2739 68857643 Michael Hanselmann
    raise errors.GenericError("Invalid salt: %r" % salt)
2740 68857643 Michael Hanselmann
2741 68857643 Michael Hanselmann
  # Dumping as PEM here ensures the certificate is in a sane format
2742 68857643 Michael Hanselmann
  cert_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
2743 68857643 Michael Hanselmann
2744 68857643 Michael Hanselmann
  return ("%s: %s/%s\n\n%s" %
2745 68857643 Michael Hanselmann
          (constants.X509_CERT_SIGNATURE_HEADER, salt,
2746 3718bf6d Michael Hanselmann
           Sha1Hmac(key, cert_pem, salt=salt),
2747 68857643 Michael Hanselmann
           cert_pem))
2748 68857643 Michael Hanselmann
2749 68857643 Michael Hanselmann
2750 68857643 Michael Hanselmann
def _ExtractX509CertificateSignature(cert_pem):
2751 68857643 Michael Hanselmann
  """Helper function to extract signature from X509 certificate.
2752 68857643 Michael Hanselmann

2753 68857643 Michael Hanselmann
  """
2754 68857643 Michael Hanselmann
  # Extract signature from original PEM data
2755 68857643 Michael Hanselmann
  for line in cert_pem.splitlines():
2756 68857643 Michael Hanselmann
    if line.startswith("---"):
2757 68857643 Michael Hanselmann
      break
2758 68857643 Michael Hanselmann
2759 68857643 Michael Hanselmann
    m = X509_SIGNATURE.match(line.strip())
2760 68857643 Michael Hanselmann
    if m:
2761 68857643 Michael Hanselmann
      return (m.group("salt"), m.group("sign"))
2762 68857643 Michael Hanselmann
2763 68857643 Michael Hanselmann
  raise errors.GenericError("X509 certificate signature is missing")
2764 68857643 Michael Hanselmann
2765 68857643 Michael Hanselmann
2766 68857643 Michael Hanselmann
def LoadSignedX509Certificate(cert_pem, key):
2767 68857643 Michael Hanselmann
  """Verifies a signed X509 certificate.
2768 68857643 Michael Hanselmann

2769 68857643 Michael Hanselmann
  @type cert_pem: string
2770 68857643 Michael Hanselmann
  @param cert_pem: Certificate in PEM format and with signature header
2771 68857643 Michael Hanselmann
  @type key: string
2772 68857643 Michael Hanselmann
  @param key: Key for HMAC
2773 68857643 Michael Hanselmann
  @rtype: tuple; (OpenSSL.crypto.X509, string)
2774 68857643 Michael Hanselmann
  @return: X509 certificate object and salt
2775 68857643 Michael Hanselmann

2776 68857643 Michael Hanselmann
  """
2777 68857643 Michael Hanselmann
  (salt, signature) = _ExtractX509CertificateSignature(cert_pem)
2778 68857643 Michael Hanselmann
2779 68857643 Michael Hanselmann
  # Load certificate
2780 68857643 Michael Hanselmann
  cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_pem)
2781 68857643 Michael Hanselmann
2782 68857643 Michael Hanselmann
  # Dump again to ensure it's in a sane format
2783 68857643 Michael Hanselmann
  sane_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
2784 68857643 Michael Hanselmann
2785 3718bf6d Michael Hanselmann
  if not VerifySha1Hmac(key, sane_pem, signature, salt=salt):
2786 68857643 Michael Hanselmann
    raise errors.GenericError("X509 certificate signature is invalid")
2787 68857643 Michael Hanselmann
2788 68857643 Michael Hanselmann
  return (cert, salt)
2789 68857643 Michael Hanselmann
2790 68857643 Michael Hanselmann
2791 3718bf6d Michael Hanselmann
def Sha1Hmac(key, text, salt=None):
2792 615aaaba Michael Hanselmann
  """Calculates the HMAC-SHA1 digest of a text.
2793 615aaaba Michael Hanselmann

2794 615aaaba Michael Hanselmann
  HMAC is defined in RFC2104.
2795 615aaaba Michael Hanselmann

2796 615aaaba Michael Hanselmann
  @type key: string
2797 615aaaba Michael Hanselmann
  @param key: Secret key
2798 615aaaba Michael Hanselmann
  @type text: string
2799 615aaaba Michael Hanselmann

2800 615aaaba Michael Hanselmann
  """
2801 3718bf6d Michael Hanselmann
  if salt:
2802 3718bf6d Michael Hanselmann
    salted_text = salt + text
2803 3718bf6d Michael Hanselmann
  else:
2804 3718bf6d Michael Hanselmann
    salted_text = text
2805 3718bf6d Michael Hanselmann
2806 716a32cb Guido Trotter
  return hmac.new(key, salted_text, compat.sha1).hexdigest()
2807 615aaaba Michael Hanselmann
2808 615aaaba Michael Hanselmann
2809 3718bf6d Michael Hanselmann
def VerifySha1Hmac(key, text, digest, salt=None):
2810 615aaaba Michael Hanselmann
  """Verifies the HMAC-SHA1 digest of a text.
2811 615aaaba Michael Hanselmann

2812 615aaaba Michael Hanselmann
  HMAC is defined in RFC2104.
2813 615aaaba Michael Hanselmann

2814 615aaaba Michael Hanselmann
  @type key: string
2815 615aaaba Michael Hanselmann
  @param key: Secret key
2816 615aaaba Michael Hanselmann
  @type text: string
2817 615aaaba Michael Hanselmann
  @type digest: string
2818 615aaaba Michael Hanselmann
  @param digest: Expected digest
2819 615aaaba Michael Hanselmann
  @rtype: bool
2820 615aaaba Michael Hanselmann
  @return: Whether HMAC-SHA1 digest matches
2821 615aaaba Michael Hanselmann

2822 615aaaba Michael Hanselmann
  """
2823 3718bf6d Michael Hanselmann
  return digest.lower() == Sha1Hmac(key, text, salt=salt).lower()
2824 615aaaba Michael Hanselmann
2825 615aaaba Michael Hanselmann
2826 26f15862 Iustin Pop
def SafeEncode(text):
2827 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
2828 26f15862 Iustin Pop

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

2838 26f15862 Iustin Pop
  @type text: str or unicode
2839 26f15862 Iustin Pop
  @param text: input data
2840 26f15862 Iustin Pop
  @rtype: str
2841 26f15862 Iustin Pop
  @return: a safe version of text
2842 26f15862 Iustin Pop

2843 26f15862 Iustin Pop
  """
2844 d392fa34 Iustin Pop
  if isinstance(text, unicode):
2845 5bbd3f7f Michael Hanselmann
    # only if unicode; if str already, we handle it below
2846 d392fa34 Iustin Pop
    text = text.encode('ascii', 'backslashreplace')
2847 d392fa34 Iustin Pop
  resu = ""
2848 d392fa34 Iustin Pop
  for char in text:
2849 d392fa34 Iustin Pop
    c = ord(char)
2850 d392fa34 Iustin Pop
    if char  == '\t':
2851 d392fa34 Iustin Pop
      resu += r'\t'
2852 d392fa34 Iustin Pop
    elif char == '\n':
2853 d392fa34 Iustin Pop
      resu += r'\n'
2854 d392fa34 Iustin Pop
    elif char == '\r':
2855 d392fa34 Iustin Pop
      resu += r'\'r'
2856 d392fa34 Iustin Pop
    elif c < 32 or c >= 127: # non-printable
2857 d392fa34 Iustin Pop
      resu += "\\x%02x" % (c & 0xff)
2858 d392fa34 Iustin Pop
    else:
2859 d392fa34 Iustin Pop
      resu += char
2860 d392fa34 Iustin Pop
  return resu
2861 26f15862 Iustin Pop
2862 26f15862 Iustin Pop
2863 5b69bc7c Iustin Pop
def UnescapeAndSplit(text, sep=","):
2864 5b69bc7c Iustin Pop
  """Split and unescape a string based on a given separator.
2865 5b69bc7c Iustin Pop

2866 5b69bc7c Iustin Pop
  This function splits a string based on a separator where the
2867 5b69bc7c Iustin Pop
  separator itself can be escape in order to be an element of the
2868 5b69bc7c Iustin Pop
  elements. The escaping rules are (assuming coma being the
2869 5b69bc7c Iustin Pop
  separator):
2870 5b69bc7c Iustin Pop
    - a plain , separates the elements
2871 5b69bc7c Iustin Pop
    - a sequence \\\\, (double backslash plus comma) is handled as a
2872 5b69bc7c Iustin Pop
      backslash plus a separator comma
2873 5b69bc7c Iustin Pop
    - a sequence \, (backslash plus comma) is handled as a
2874 5b69bc7c Iustin Pop
      non-separator comma
2875 5b69bc7c Iustin Pop

2876 5b69bc7c Iustin Pop
  @type text: string
2877 5b69bc7c Iustin Pop
  @param text: the string to split
2878 5b69bc7c Iustin Pop
  @type sep: string
2879 5b69bc7c Iustin Pop
  @param text: the separator
2880 5b69bc7c Iustin Pop
  @rtype: string
2881 5b69bc7c Iustin Pop
  @return: a list of strings
2882 5b69bc7c Iustin Pop

2883 5b69bc7c Iustin Pop
  """
2884 5b69bc7c Iustin Pop
  # we split the list by sep (with no escaping at this stage)
2885 5b69bc7c Iustin Pop
  slist = text.split(sep)
2886 5b69bc7c Iustin Pop
  # next, we revisit the elements and if any of them ended with an odd
2887 5b69bc7c Iustin Pop
  # number of backslashes, then we join it with the next
2888 5b69bc7c Iustin Pop
  rlist = []
2889 5b69bc7c Iustin Pop
  while slist:
2890 5b69bc7c Iustin Pop
    e1 = slist.pop(0)
2891 5b69bc7c Iustin Pop
    if e1.endswith("\\"):
2892 5b69bc7c Iustin Pop
      num_b = len(e1) - len(e1.rstrip("\\"))
2893 5b69bc7c Iustin Pop
      if num_b % 2 == 1:
2894 5b69bc7c Iustin Pop
        e2 = slist.pop(0)
2895 5b69bc7c Iustin Pop
        # here the backslashes remain (all), and will be reduced in
2896 5b69bc7c Iustin Pop
        # the next step
2897 5b69bc7c Iustin Pop
        rlist.append(e1 + sep + e2)
2898 5b69bc7c Iustin Pop
        continue
2899 5b69bc7c Iustin Pop
    rlist.append(e1)
2900 5b69bc7c Iustin Pop
  # finally, replace backslash-something with something
2901 5b69bc7c Iustin Pop
  rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist]
2902 5b69bc7c Iustin Pop
  return rlist
2903 5b69bc7c Iustin Pop
2904 5b69bc7c Iustin Pop
2905 ab3e6da8 Iustin Pop
def CommaJoin(names):
2906 ab3e6da8 Iustin Pop
  """Nicely join a set of identifiers.
2907 ab3e6da8 Iustin Pop

2908 ab3e6da8 Iustin Pop
  @param names: set, list or tuple
2909 ab3e6da8 Iustin Pop
  @return: a string with the formatted results
2910 ab3e6da8 Iustin Pop

2911 ab3e6da8 Iustin Pop
  """
2912 1f864b60 Iustin Pop
  return ", ".join([str(val) for val in names])
2913 ab3e6da8 Iustin Pop
2914 ab3e6da8 Iustin Pop
2915 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
2916 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
2917 3f6a47a8 Michael Hanselmann

2918 3f6a47a8 Michael Hanselmann
  @type value: int
2919 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
2920 3f6a47a8 Michael Hanselmann
  @rtype: int
2921 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
2922 3f6a47a8 Michael Hanselmann

2923 3f6a47a8 Michael Hanselmann
  """
2924 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
2925 3f6a47a8 Michael Hanselmann
2926 3f6a47a8 Michael Hanselmann
2927 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
2928 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
2929 3f6a47a8 Michael Hanselmann

2930 3f6a47a8 Michael Hanselmann
  @type path: string
2931 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
2932 3f6a47a8 Michael Hanselmann
  @rtype: int
2933 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
2934 3f6a47a8 Michael Hanselmann

2935 3f6a47a8 Michael Hanselmann
  """
2936 3f6a47a8 Michael Hanselmann
  size = 0
2937 3f6a47a8 Michael Hanselmann
2938 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
2939 2a887df9 Michael Hanselmann
    for filename in files:
2940 c4feafe8 Iustin Pop
      st = os.lstat(PathJoin(curpath, filename))
2941 3f6a47a8 Michael Hanselmann
      size += st.st_size
2942 3f6a47a8 Michael Hanselmann
2943 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
2944 3f6a47a8 Michael Hanselmann
2945 3f6a47a8 Michael Hanselmann
2946 1b045f5d Balazs Lecz
def GetMounts(filename=constants.PROC_MOUNTS):
2947 1b045f5d Balazs Lecz
  """Returns the list of mounted filesystems.
2948 1b045f5d Balazs Lecz

2949 1b045f5d Balazs Lecz
  This function is Linux-specific.
2950 1b045f5d Balazs Lecz

2951 1b045f5d Balazs Lecz
  @param filename: path of mounts file (/proc/mounts by default)
2952 1b045f5d Balazs Lecz
  @rtype: list of tuples
2953 1b045f5d Balazs Lecz
  @return: list of mount entries (device, mountpoint, fstype, options)
2954 1b045f5d Balazs Lecz

2955 1b045f5d Balazs Lecz
  """
2956 1b045f5d Balazs Lecz
  # TODO(iustin): investigate non-Linux options (e.g. via mount output)
2957 1b045f5d Balazs Lecz
  data = []
2958 1b045f5d Balazs Lecz
  mountlines = ReadFile(filename).splitlines()
2959 1b045f5d Balazs Lecz
  for line in mountlines:
2960 1b045f5d Balazs Lecz
    device, mountpoint, fstype, options, _ = line.split(None, 4)
2961 1b045f5d Balazs Lecz
    data.append((device, mountpoint, fstype, options))
2962 1b045f5d Balazs Lecz
2963 1b045f5d Balazs Lecz
  return data
2964 1b045f5d Balazs Lecz
2965 1b045f5d Balazs Lecz
2966 620a85fd Iustin Pop
def GetFilesystemStats(path):
2967 620a85fd Iustin Pop
  """Returns the total and free space on a filesystem.
2968 3f6a47a8 Michael Hanselmann

2969 3f6a47a8 Michael Hanselmann
  @type path: string
2970 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
2971 3f6a47a8 Michael Hanselmann
  @rtype: int
2972 620a85fd Iustin Pop
  @return: tuple of (Total space, Free space) in mebibytes
2973 3f6a47a8 Michael Hanselmann

2974 3f6a47a8 Michael Hanselmann
  """
2975 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
2976 3f6a47a8 Michael Hanselmann
2977 620a85fd Iustin Pop
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
2978 620a85fd Iustin Pop
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
2979 620a85fd Iustin Pop
  return (tsize, fsize)
2980 3f6a47a8 Michael Hanselmann
2981 3f6a47a8 Michael Hanselmann
2982 bdefe5dd Michael Hanselmann
def RunInSeparateProcess(fn, *args):
2983 eb58f7bd Michael Hanselmann
  """Runs a function in a separate process.
2984 eb58f7bd Michael Hanselmann

2985 eb58f7bd Michael Hanselmann
  Note: Only boolean return values are supported.
2986 eb58f7bd Michael Hanselmann

2987 eb58f7bd Michael Hanselmann
  @type fn: callable
2988 eb58f7bd Michael Hanselmann
  @param fn: Function to be called
2989 bdefe5dd Michael Hanselmann
  @rtype: bool
2990 bdefe5dd Michael Hanselmann
  @return: Function's result
2991 eb58f7bd Michael Hanselmann

2992 eb58f7bd Michael Hanselmann
  """
2993 eb58f7bd Michael Hanselmann
  pid = os.fork()
2994 eb58f7bd Michael Hanselmann
  if pid == 0:
2995 eb58f7bd Michael Hanselmann
    # Child process
2996 eb58f7bd Michael Hanselmann
    try:
2997 82869978 Michael Hanselmann
      # In case the function uses temporary files
2998 82869978 Michael Hanselmann
      ResetTempfileModule()
2999 82869978 Michael Hanselmann
3000 eb58f7bd Michael Hanselmann
      # Call function
3001 bdefe5dd Michael Hanselmann
      result = int(bool(fn(*args)))
3002 eb58f7bd Michael Hanselmann
      assert result in (0, 1)
3003 eb58f7bd Michael Hanselmann
    except: # pylint: disable-msg=W0702
3004 eb58f7bd Michael Hanselmann
      logging.exception("Error while calling function in separate process")
3005 eb58f7bd Michael Hanselmann
      # 0 and 1 are reserved for the return value
3006 eb58f7bd Michael Hanselmann
      result = 33
3007 eb58f7bd Michael Hanselmann
3008 eb58f7bd Michael Hanselmann
    os._exit(result) # pylint: disable-msg=W0212
3009 eb58f7bd Michael Hanselmann
3010 eb58f7bd Michael Hanselmann
  # Parent process
3011 eb58f7bd Michael Hanselmann
3012 eb58f7bd Michael Hanselmann
  # Avoid zombies and check exit code
3013 eb58f7bd Michael Hanselmann
  (_, status) = os.waitpid(pid, 0)
3014 eb58f7bd Michael Hanselmann
3015 eb58f7bd Michael Hanselmann
  if os.WIFSIGNALED(status):
3016 eb58f7bd Michael Hanselmann
    exitcode = None
3017 eb58f7bd Michael Hanselmann
    signum = os.WTERMSIG(status)
3018 eb58f7bd Michael Hanselmann
  else:
3019 eb58f7bd Michael Hanselmann
    exitcode = os.WEXITSTATUS(status)
3020 eb58f7bd Michael Hanselmann
    signum = None
3021 eb58f7bd Michael Hanselmann
3022 eb58f7bd Michael Hanselmann
  if not (exitcode in (0, 1) and signum is None):
3023 eb58f7bd Michael Hanselmann
    raise errors.GenericError("Child program failed (code=%s, signal=%s)" %
3024 eb58f7bd Michael Hanselmann
                              (exitcode, signum))
3025 eb58f7bd Michael Hanselmann
3026 eb58f7bd Michael Hanselmann
  return bool(exitcode)
3027 eb58f7bd Michael Hanselmann
3028 eb58f7bd Michael Hanselmann
3029 560cbec1 Michael Hanselmann
def IgnoreProcessNotFound(fn, *args, **kwargs):
3030 560cbec1 Michael Hanselmann
  """Ignores ESRCH when calling a process-related function.
3031 560cbec1 Michael Hanselmann

3032 560cbec1 Michael Hanselmann
  ESRCH is raised when a process is not found.
3033 560cbec1 Michael Hanselmann

3034 560cbec1 Michael Hanselmann
  @rtype: bool
3035 560cbec1 Michael Hanselmann
  @return: Whether process was found
3036 560cbec1 Michael Hanselmann

3037 560cbec1 Michael Hanselmann
  """
3038 560cbec1 Michael Hanselmann
  try:
3039 560cbec1 Michael Hanselmann
    fn(*args, **kwargs)
3040 560cbec1 Michael Hanselmann
  except EnvironmentError, err:
3041 560cbec1 Michael Hanselmann
    # Ignore ESRCH
3042 560cbec1 Michael Hanselmann
    if err.errno == errno.ESRCH:
3043 560cbec1 Michael Hanselmann
      return False
3044 560cbec1 Michael Hanselmann
    raise
3045 560cbec1 Michael Hanselmann
3046 560cbec1 Michael Hanselmann
  return True
3047 560cbec1 Michael Hanselmann
3048 560cbec1 Michael Hanselmann
3049 232144d0 Guido Trotter
def IgnoreSignals(fn, *args, **kwargs):
3050 232144d0 Guido Trotter
  """Tries to call a function ignoring failures due to EINTR.
3051 232144d0 Guido Trotter

3052 232144d0 Guido Trotter
  """
3053 232144d0 Guido Trotter
  try:
3054 232144d0 Guido Trotter
    return fn(*args, **kwargs)
3055 965d0e5b Guido Trotter
  except EnvironmentError, err:
3056 2fd7f564 Guido Trotter
    if err.errno == errno.EINTR:
3057 2fd7f564 Guido Trotter
      return None
3058 2fd7f564 Guido Trotter
    else:
3059 232144d0 Guido Trotter
      raise
3060 965d0e5b Guido Trotter
  except (select.error, socket.error), err:
3061 965d0e5b Guido Trotter
    # In python 2.6 and above select.error is an IOError, so it's handled
3062 965d0e5b Guido Trotter
    # above, in 2.5 and below it's not, and it's handled here.
3063 2fd7f564 Guido Trotter
    if err.args and err.args[0] == errno.EINTR:
3064 2fd7f564 Guido Trotter
      return None
3065 2fd7f564 Guido Trotter
    else:
3066 232144d0 Guido Trotter
      raise
3067 232144d0 Guido Trotter
3068 232144d0 Guido Trotter
3069 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
3070 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
3071 eb0f0ce0 Michael Hanselmann

3072 58885d79 Iustin Pop
  @type fd: int
3073 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
3074 58885d79 Iustin Pop

3075 eb0f0ce0 Michael Hanselmann
  """
3076 eb0f0ce0 Michael Hanselmann
  try:
3077 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
3078 eb0f0ce0 Michael Hanselmann
  except IOError, err:
3079 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
3080 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
3081 eb0f0ce0 Michael Hanselmann
    raise
3082 de499029 Michael Hanselmann
3083 de499029 Michael Hanselmann
3084 3b813dd2 Iustin Pop
def FormatTime(val):
3085 3b813dd2 Iustin Pop
  """Formats a time value.
3086 3b813dd2 Iustin Pop

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

3091 3b813dd2 Iustin Pop
  """
3092 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
3093 3b813dd2 Iustin Pop
    return "N/A"
3094 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
3095 3b813dd2 Iustin Pop
  # platforms
3096 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
3097 3b813dd2 Iustin Pop
3098 3b813dd2 Iustin Pop
3099 f8ea4ada Michael Hanselmann
def FormatSeconds(secs):
3100 f8ea4ada Michael Hanselmann
  """Formats seconds for easier reading.
3101 f8ea4ada Michael Hanselmann

3102 f8ea4ada Michael Hanselmann
  @type secs: number
3103 f8ea4ada Michael Hanselmann
  @param secs: Number of seconds
3104 f8ea4ada Michael Hanselmann
  @rtype: string
3105 f8ea4ada Michael Hanselmann
  @return: Formatted seconds (e.g. "2d 9h 19m 49s")
3106 f8ea4ada Michael Hanselmann

3107 f8ea4ada Michael Hanselmann
  """
3108 f8ea4ada Michael Hanselmann
  parts = []
3109 f8ea4ada Michael Hanselmann
3110 f8ea4ada Michael Hanselmann
  secs = round(secs, 0)
3111 f8ea4ada Michael Hanselmann
3112 f8ea4ada Michael Hanselmann
  if secs > 0:
3113 f8ea4ada Michael Hanselmann
    # Negative values would be a bit tricky
3114 f8ea4ada Michael Hanselmann
    for unit, one in [("d", 24 * 60 * 60), ("h", 60 * 60), ("m", 60)]:
3115 f8ea4ada Michael Hanselmann
      (complete, secs) = divmod(secs, one)
3116 f8ea4ada Michael Hanselmann
      if complete or parts:
3117 f8ea4ada Michael Hanselmann
        parts.append("%d%s" % (complete, unit))
3118 f8ea4ada Michael Hanselmann
3119 f8ea4ada Michael Hanselmann
  parts.append("%ds" % secs)
3120 f8ea4ada Michael Hanselmann
3121 f8ea4ada Michael Hanselmann
  return " ".join(parts)
3122 f8ea4ada Michael Hanselmann
3123 f8ea4ada Michael Hanselmann
3124 5cbe43a5 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
3125 05e50653 Michael Hanselmann
  """Reads the watcher pause file.
3126 05e50653 Michael Hanselmann

3127 5cbe43a5 Michael Hanselmann
  @type filename: string
3128 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
3129 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
3130 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
3131 5cbe43a5 Michael Hanselmann
  @type remove_after: int
3132 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
3133 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
3134 5cbe43a5 Michael Hanselmann

3135 05e50653 Michael Hanselmann
  """
3136 05e50653 Michael Hanselmann
  if now is None:
3137 05e50653 Michael Hanselmann
    now = time.time()
3138 05e50653 Michael Hanselmann
3139 05e50653 Michael Hanselmann
  try:
3140 05e50653 Michael Hanselmann
    value = ReadFile(filename)
3141 05e50653 Michael Hanselmann
  except IOError, err:
3142 05e50653 Michael Hanselmann
    if err.errno != errno.ENOENT:
3143 05e50653 Michael Hanselmann
      raise
3144 05e50653 Michael Hanselmann
    value = None
3145 05e50653 Michael Hanselmann
3146 05e50653 Michael Hanselmann
  if value is not None:
3147 05e50653 Michael Hanselmann
    try:
3148 05e50653 Michael Hanselmann
      value = int(value)
3149 05e50653 Michael Hanselmann
    except ValueError:
3150 5cbe43a5 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
3151 5cbe43a5 Michael Hanselmann
                       " removing it"), filename)
3152 5cbe43a5 Michael Hanselmann
      RemoveFile(filename)
3153 05e50653 Michael Hanselmann
      value = None
3154 05e50653 Michael Hanselmann
3155 05e50653 Michael Hanselmann
    if value is not None:
3156 5cbe43a5 Michael Hanselmann
      # Remove file if it's outdated
3157 5cbe43a5 Michael Hanselmann
      if now > (value + remove_after):
3158 5cbe43a5 Michael Hanselmann
        RemoveFile(filename)
3159 5cbe43a5 Michael Hanselmann
        value = None
3160 5cbe43a5 Michael Hanselmann
3161 5cbe43a5 Michael Hanselmann
      elif now > value:
3162 05e50653 Michael Hanselmann
        value = None
3163 05e50653 Michael Hanselmann
3164 05e50653 Michael Hanselmann
  return value
3165 05e50653 Michael Hanselmann
3166 05e50653 Michael Hanselmann
3167 de0ea66b Michael Hanselmann
class RetryTimeout(Exception):
3168 de0ea66b Michael Hanselmann
  """Retry loop timed out.
3169 de0ea66b Michael Hanselmann

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

3174 de0ea66b Michael Hanselmann
  """
3175 506be7c5 Guido Trotter
  def RaiseInner(self):
3176 506be7c5 Guido Trotter
    if self.args and isinstance(self.args[0], Exception):
3177 506be7c5 Guido Trotter
      raise self.args[0]
3178 506be7c5 Guido Trotter
    else:
3179 506be7c5 Guido Trotter
      raise RetryTimeout(*self.args)
3180 de0ea66b Michael Hanselmann
3181 de0ea66b Michael Hanselmann
3182 de0ea66b Michael Hanselmann
class RetryAgain(Exception):
3183 de0ea66b Michael Hanselmann
  """Retry again.
3184 de0ea66b Michael Hanselmann

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

3189 de0ea66b Michael Hanselmann
  """
3190 de0ea66b Michael Hanselmann
3191 de0ea66b Michael Hanselmann
3192 de0ea66b Michael Hanselmann
class _RetryDelayCalculator(object):
3193 de0ea66b Michael Hanselmann
  """Calculator for increasing delays.
3194 de0ea66b Michael Hanselmann

3195 de0ea66b Michael Hanselmann
  """
3196 de0ea66b Michael Hanselmann
  __slots__ = [
3197 de0ea66b Michael Hanselmann
    "_factor",
3198 de0ea66b Michael Hanselmann
    "_limit",
3199 de0ea66b Michael Hanselmann
    "_next",
3200 de0ea66b Michael Hanselmann
    "_start",
3201 de0ea66b Michael Hanselmann
    ]
3202 de0ea66b Michael Hanselmann
3203 de0ea66b Michael Hanselmann
  def __init__(self, start, factor, limit):
3204 de0ea66b Michael Hanselmann
    """Initializes this class.
3205 de0ea66b Michael Hanselmann

3206 de0ea66b Michael Hanselmann
    @type start: float
3207 de0ea66b Michael Hanselmann
    @param start: Initial delay
3208 de0ea66b Michael Hanselmann
    @type factor: float
3209 de0ea66b Michael Hanselmann
    @param factor: Factor for delay increase
3210 de0ea66b Michael Hanselmann
    @type limit: float or None
3211 de0ea66b Michael Hanselmann
    @param limit: Upper limit for delay or None for no limit
3212 de0ea66b Michael Hanselmann

3213 de0ea66b Michael Hanselmann
    """
3214 de0ea66b Michael Hanselmann
    assert start > 0.0
3215 de0ea66b Michael Hanselmann
    assert factor >= 1.0
3216 de0ea66b Michael Hanselmann
    assert limit is None or limit >= 0.0
3217 de0ea66b Michael Hanselmann
3218 de0ea66b Michael Hanselmann
    self._start = start
3219 de0ea66b Michael Hanselmann
    self._factor = factor
3220 de0ea66b Michael Hanselmann
    self._limit = limit
3221 de0ea66b Michael Hanselmann
3222 de0ea66b Michael Hanselmann
    self._next = start
3223 de0ea66b Michael Hanselmann
3224 de0ea66b Michael Hanselmann
  def __call__(self):
3225 de0ea66b Michael Hanselmann
    """Returns current delay and calculates the next one.
3226 de0ea66b Michael Hanselmann

3227 de0ea66b Michael Hanselmann
    """
3228 de0ea66b Michael Hanselmann
    current = self._next
3229 de0ea66b Michael Hanselmann
3230 de0ea66b Michael Hanselmann
    # Update for next run
3231 de0ea66b Michael Hanselmann
    if self._limit is None or self._next < self._limit:
3232 26751075 Michael Hanselmann
      self._next = min(self._limit, self._next * self._factor)
3233 de0ea66b Michael Hanselmann
3234 de0ea66b Michael Hanselmann
    return current
3235 de0ea66b Michael Hanselmann
3236 de0ea66b Michael Hanselmann
3237 de0ea66b Michael Hanselmann
#: Special delay to specify whole remaining timeout
3238 de0ea66b Michael Hanselmann
RETRY_REMAINING_TIME = object()
3239 de0ea66b Michael Hanselmann
3240 de0ea66b Michael Hanselmann
3241 de0ea66b Michael Hanselmann
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep,
3242 de0ea66b Michael Hanselmann
          _time_fn=time.time):
3243 de0ea66b Michael Hanselmann
  """Call a function repeatedly until it succeeds.
3244 de0ea66b Michael Hanselmann

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

3249 de0ea66b Michael Hanselmann
  C{delay} can be one of the following:
3250 de0ea66b Michael Hanselmann
    - callable returning the delay length as a float
3251 de0ea66b Michael Hanselmann
    - Tuple of (start, factor, limit)
3252 de0ea66b Michael Hanselmann
    - L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is
3253 de0ea66b Michael Hanselmann
      useful when overriding L{wait_fn} to wait for an external event)
3254 de0ea66b Michael Hanselmann
    - A static delay as a number (int or float)
3255 de0ea66b Michael Hanselmann

3256 de0ea66b Michael Hanselmann
  @type fn: callable
3257 de0ea66b Michael Hanselmann
  @param fn: Function to be called
3258 de0ea66b Michael Hanselmann
  @param delay: Either a callable (returning the delay), a tuple of (start,
3259 de0ea66b Michael Hanselmann
                factor, limit) (see L{_RetryDelayCalculator}),
3260 de0ea66b Michael Hanselmann
                L{RETRY_REMAINING_TIME} or a number (int or float)
3261 de0ea66b Michael Hanselmann
  @type timeout: float
3262 de0ea66b Michael Hanselmann
  @param timeout: Total timeout
3263 de0ea66b Michael Hanselmann
  @type wait_fn: callable
3264 de0ea66b Michael Hanselmann
  @param wait_fn: Waiting function
3265 de0ea66b Michael Hanselmann
  @return: Return value of function
3266 de0ea66b Michael Hanselmann

3267 de0ea66b Michael Hanselmann
  """
3268 de0ea66b Michael Hanselmann
  assert callable(fn)
3269 de0ea66b Michael Hanselmann
  assert callable(wait_fn)
3270 de0ea66b Michael Hanselmann
  assert callable(_time_fn)
3271 de0ea66b Michael Hanselmann
3272 de0ea66b Michael Hanselmann
  if args is None:
3273 de0ea66b Michael Hanselmann
    args = []
3274 de0ea66b Michael Hanselmann
3275 de0ea66b Michael Hanselmann
  end_time = _time_fn() + timeout
3276 de0ea66b Michael Hanselmann
3277 de0ea66b Michael Hanselmann
  if callable(delay):
3278 de0ea66b Michael Hanselmann
    # External function to calculate delay
3279 de0ea66b Michael Hanselmann
    calc_delay = delay
3280 de0ea66b Michael Hanselmann
3281 de0ea66b Michael Hanselmann
  elif isinstance(delay, (tuple, list)):
3282 de0ea66b Michael Hanselmann
    # Increasing delay with optional upper boundary
3283 de0ea66b Michael Hanselmann
    (start, factor, limit) = delay
3284 de0ea66b Michael Hanselmann
    calc_delay = _RetryDelayCalculator(start, factor, limit)
3285 de0ea66b Michael Hanselmann
3286 de0ea66b Michael Hanselmann
  elif delay is RETRY_REMAINING_TIME:
3287 de0ea66b Michael Hanselmann
    # Always use the remaining time
3288 de0ea66b Michael Hanselmann
    calc_delay = None
3289 de0ea66b Michael Hanselmann
3290 de0ea66b Michael Hanselmann
  else:
3291 de0ea66b Michael Hanselmann
    # Static delay
3292 de0ea66b Michael Hanselmann
    calc_delay = lambda: delay
3293 de0ea66b Michael Hanselmann
3294 de0ea66b Michael Hanselmann
  assert calc_delay is None or callable(calc_delay)
3295 de0ea66b Michael Hanselmann
3296 de0ea66b Michael Hanselmann
  while True:
3297 506be7c5 Guido Trotter
    retry_args = []
3298 de0ea66b Michael Hanselmann
    try:
3299 7260cfbe Iustin Pop
      # pylint: disable-msg=W0142
3300 de0ea66b Michael Hanselmann
      return fn(*args)
3301 506be7c5 Guido Trotter
    except RetryAgain, err:
3302 506be7c5 Guido Trotter
      retry_args = err.args
3303 1b429e2a Iustin Pop
    except RetryTimeout:
3304 1b429e2a Iustin Pop
      raise errors.ProgrammerError("Nested retry loop detected that didn't"
3305 1b429e2a Iustin Pop
                                   " handle RetryTimeout")
3306 de0ea66b Michael Hanselmann
3307 de0ea66b Michael Hanselmann
    remaining_time = end_time - _time_fn()
3308 de0ea66b Michael Hanselmann
3309 de0ea66b Michael Hanselmann
    if remaining_time < 0.0:
3310 506be7c5 Guido Trotter
      # pylint: disable-msg=W0142
3311 506be7c5 Guido Trotter
      raise RetryTimeout(*retry_args)
3312 de0ea66b Michael Hanselmann
3313 de0ea66b Michael Hanselmann
    assert remaining_time >= 0.0
3314 de0ea66b Michael Hanselmann
3315 de0ea66b Michael Hanselmann
    if calc_delay is None:
3316 de0ea66b Michael Hanselmann
      wait_fn(remaining_time)
3317 de0ea66b Michael Hanselmann
    else:
3318 de0ea66b Michael Hanselmann
      current_delay = calc_delay()
3319 de0ea66b Michael Hanselmann
      if current_delay > 0.0:
3320 de0ea66b Michael Hanselmann
        wait_fn(current_delay)
3321 de0ea66b Michael Hanselmann
3322 de0ea66b Michael Hanselmann
3323 bdd5e420 Michael Hanselmann
def GetClosedTempfile(*args, **kwargs):
3324 bdd5e420 Michael Hanselmann
  """Creates a temporary file and returns its path.
3325 a55474c7 Michael Hanselmann

3326 bdd5e420 Michael Hanselmann
  """
3327 bdd5e420 Michael Hanselmann
  (fd, path) = tempfile.mkstemp(*args, **kwargs)
3328 bdd5e420 Michael Hanselmann
  _CloseFDNoErr(fd)
3329 bdd5e420 Michael Hanselmann
  return path
3330 bdd5e420 Michael Hanselmann
3331 bdd5e420 Michael Hanselmann
3332 bdd5e420 Michael Hanselmann
def GenerateSelfSignedX509Cert(common_name, validity):
3333 bdd5e420 Michael Hanselmann
  """Generates a self-signed X509 certificate.
3334 bdd5e420 Michael Hanselmann

3335 bdd5e420 Michael Hanselmann
  @type common_name: string
3336 bdd5e420 Michael Hanselmann
  @param common_name: commonName value
3337 a55474c7 Michael Hanselmann
  @type validity: int
3338 bdd5e420 Michael Hanselmann
  @param validity: Validity for certificate in seconds
3339 a55474c7 Michael Hanselmann

3340 a55474c7 Michael Hanselmann
  """
3341 bdd5e420 Michael Hanselmann
  # Create private and public key
3342 bdd5e420 Michael Hanselmann
  key = OpenSSL.crypto.PKey()
3343 bdd5e420 Michael Hanselmann
  key.generate_key(OpenSSL.crypto.TYPE_RSA, constants.RSA_KEY_BITS)
3344 bdd5e420 Michael Hanselmann
3345 bdd5e420 Michael Hanselmann
  # Create self-signed certificate
3346 bdd5e420 Michael Hanselmann
  cert = OpenSSL.crypto.X509()
3347 bdd5e420 Michael Hanselmann
  if common_name:
3348 bdd5e420 Michael Hanselmann
    cert.get_subject().CN = common_name
3349 bdd5e420 Michael Hanselmann
  cert.set_serial_number(1)
3350 bdd5e420 Michael Hanselmann
  cert.gmtime_adj_notBefore(0)
3351 bdd5e420 Michael Hanselmann
  cert.gmtime_adj_notAfter(validity)
3352 bdd5e420 Michael Hanselmann
  cert.set_issuer(cert.get_subject())
3353 bdd5e420 Michael Hanselmann
  cert.set_pubkey(key)
3354 bdd5e420 Michael Hanselmann
  cert.sign(key, constants.X509_CERT_SIGN_DIGEST)
3355 bdd5e420 Michael Hanselmann
3356 bdd5e420 Michael Hanselmann
  key_pem = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key)
3357 bdd5e420 Michael Hanselmann
  cert_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
3358 bdd5e420 Michael Hanselmann
3359 bdd5e420 Michael Hanselmann
  return (key_pem, cert_pem)
3360 bdd5e420 Michael Hanselmann
3361 bdd5e420 Michael Hanselmann
3362 600535f0 Manuel Franceschini
def GenerateSelfSignedSslCert(filename, common_name=constants.X509_CERT_CN,
3363 600535f0 Manuel Franceschini
                              validity=constants.X509_CERT_DEFAULT_VALIDITY):
3364 bdd5e420 Michael Hanselmann
  """Legacy function to generate self-signed X509 certificate.
3365 bdd5e420 Michael Hanselmann

3366 2ea65c7d Manuel Franceschini
  @type filename: str
3367 2ea65c7d Manuel Franceschini
  @param filename: path to write certificate to
3368 600535f0 Manuel Franceschini
  @type common_name: string
3369 600535f0 Manuel Franceschini
  @param common_name: commonName value
3370 600535f0 Manuel Franceschini
  @type validity: int
3371 600535f0 Manuel Franceschini
  @param validity: validity of certificate in number of days
3372 600535f0 Manuel Franceschini

3373 bdd5e420 Michael Hanselmann
  """
3374 600535f0 Manuel Franceschini
  # TODO: Investigate using the cluster name instead of X505_CERT_CN for
3375 600535f0 Manuel Franceschini
  # common_name, as cluster-renames are very seldom, and it'd be nice if RAPI
3376 600535f0 Manuel Franceschini
  # and node daemon certificates have the proper Subject/Issuer.
3377 600535f0 Manuel Franceschini
  (key_pem, cert_pem) = GenerateSelfSignedX509Cert(common_name,
3378 bdd5e420 Michael Hanselmann
                                                   validity * 24 * 60 * 60)
3379 bdd5e420 Michael Hanselmann
3380 bdd5e420 Michael Hanselmann
  WriteFile(filename, mode=0400, data=key_pem + cert_pem)
3381 a55474c7 Michael Hanselmann
3382 a55474c7 Michael Hanselmann
3383 a87b4824 Michael Hanselmann
class FileLock(object):
3384 a87b4824 Michael Hanselmann
  """Utility class for file locks.
3385 a87b4824 Michael Hanselmann

3386 a87b4824 Michael Hanselmann
  """
3387 b4478d34 Michael Hanselmann
  def __init__(self, fd, filename):
3388 58885d79 Iustin Pop
    """Constructor for FileLock.
3389 58885d79 Iustin Pop

3390 b4478d34 Michael Hanselmann
    @type fd: file
3391 b4478d34 Michael Hanselmann
    @param fd: File object
3392 58885d79 Iustin Pop
    @type filename: str
3393 b4478d34 Michael Hanselmann
    @param filename: Path of the file opened at I{fd}
3394 58885d79 Iustin Pop

3395 58885d79 Iustin Pop
    """
3396 b4478d34 Michael Hanselmann
    self.fd = fd
3397 a87b4824 Michael Hanselmann
    self.filename = filename
3398 b4478d34 Michael Hanselmann
3399 b4478d34 Michael Hanselmann
  @classmethod
3400 b4478d34 Michael Hanselmann
  def Open(cls, filename):
3401 b4478d34 Michael Hanselmann
    """Creates and opens a file to be used as a file-based lock.
3402 b4478d34 Michael Hanselmann

3403 b4478d34 Michael Hanselmann
    @type filename: string
3404 b4478d34 Michael Hanselmann
    @param filename: path to the file to be locked
3405 b4478d34 Michael Hanselmann

3406 b4478d34 Michael Hanselmann
    """
3407 b4478d34 Michael Hanselmann
    # Using "os.open" is necessary to allow both opening existing file
3408 b4478d34 Michael Hanselmann
    # read/write and creating if not existing. Vanilla "open" will truncate an
3409 b4478d34 Michael Hanselmann
    # existing file -or- allow creating if not existing.
3410 b4478d34 Michael Hanselmann
    return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), "w+"),
3411 b4478d34 Michael Hanselmann
               filename)
3412 a87b4824 Michael Hanselmann
3413 a87b4824 Michael Hanselmann
  def __del__(self):
3414 a87b4824 Michael Hanselmann
    self.Close()
3415 a87b4824 Michael Hanselmann
3416 a87b4824 Michael Hanselmann
  def Close(self):
3417 58885d79 Iustin Pop
    """Close the file and release the lock.
3418 58885d79 Iustin Pop

3419 58885d79 Iustin Pop
    """
3420 aac3fbf0 Iustin Pop
    if hasattr(self, "fd") and self.fd:
3421 a87b4824 Michael Hanselmann
      self.fd.close()
3422 a87b4824 Michael Hanselmann
      self.fd = None
3423 a87b4824 Michael Hanselmann
3424 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
3425 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
3426 aa74b828 Michael Hanselmann

3427 aa74b828 Michael Hanselmann
    @type flag: int
3428 58885d79 Iustin Pop
    @param flag: operation flag
3429 aa74b828 Michael Hanselmann
    @type blocking: bool
3430 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
3431 aa74b828 Michael Hanselmann
    @type timeout: None or float
3432 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
3433 aa74b828 Michael Hanselmann
                    non-blocking mode).
3434 aa74b828 Michael Hanselmann
    @type errmsg: string
3435 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
3436 aa74b828 Michael Hanselmann

3437 aa74b828 Michael Hanselmann
    """
3438 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
3439 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
3440 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
3441 cc4c9b91 Michael Hanselmann
    assert not (flag & fcntl.LOCK_NB), "LOCK_NB must not be set"
3442 a87b4824 Michael Hanselmann
3443 cc4c9b91 Michael Hanselmann
    # When a timeout is used, LOCK_NB must always be set
3444 cc4c9b91 Michael Hanselmann
    if not (timeout is None and blocking):
3445 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
3446 a87b4824 Michael Hanselmann
3447 cc4c9b91 Michael Hanselmann
    if timeout is None:
3448 cc4c9b91 Michael Hanselmann
      self._Lock(self.fd, flag, timeout)
3449 cc4c9b91 Michael Hanselmann
    else:
3450 cc4c9b91 Michael Hanselmann
      try:
3451 cc4c9b91 Michael Hanselmann
        Retry(self._Lock, (0.1, 1.2, 1.0), timeout,
3452 cc4c9b91 Michael Hanselmann
              args=(self.fd, flag, timeout))
3453 cc4c9b91 Michael Hanselmann
      except RetryTimeout:
3454 cc4c9b91 Michael Hanselmann
        raise errors.LockError(errmsg)
3455 aa74b828 Michael Hanselmann
3456 cc4c9b91 Michael Hanselmann
  @staticmethod
3457 cc4c9b91 Michael Hanselmann
  def _Lock(fd, flag, timeout):
3458 cc4c9b91 Michael Hanselmann
    try:
3459 cc4c9b91 Michael Hanselmann
      fcntl.flock(fd, flag)
3460 cc4c9b91 Michael Hanselmann
    except IOError, err:
3461 cc4c9b91 Michael Hanselmann
      if timeout is not None and err.errno == errno.EAGAIN:
3462 cc4c9b91 Michael Hanselmann
        raise RetryAgain()
3463 31892b4c Michael Hanselmann
3464 cc4c9b91 Michael Hanselmann
      logging.exception("fcntl.flock failed")
3465 cc4c9b91 Michael Hanselmann
      raise
3466 aa74b828 Michael Hanselmann
3467 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
3468 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
3469 a87b4824 Michael Hanselmann

3470 58885d79 Iustin Pop
    @type blocking: boolean
3471 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
3472 58885d79 Iustin Pop
        can lock the file or return immediately
3473 58885d79 Iustin Pop
    @type timeout: int or None
3474 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
3475 58885d79 Iustin Pop
        (in blocking mode)
3476 58885d79 Iustin Pop

3477 a87b4824 Michael Hanselmann
    """
3478 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
3479 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
3480 a87b4824 Michael Hanselmann
3481 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
3482 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
3483 a87b4824 Michael Hanselmann

3484 58885d79 Iustin Pop
    @type blocking: boolean
3485 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
3486 58885d79 Iustin Pop
        can lock the file or return immediately
3487 58885d79 Iustin Pop
    @type timeout: int or None
3488 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
3489 58885d79 Iustin Pop
        (in blocking mode)
3490 58885d79 Iustin Pop

3491 a87b4824 Michael Hanselmann
    """
3492 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
3493 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
3494 a87b4824 Michael Hanselmann
3495 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
3496 a87b4824 Michael Hanselmann
    """Unlocks the file.
3497 a87b4824 Michael Hanselmann

3498 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
3499 58885d79 Iustin Pop
    operation::
3500 58885d79 Iustin Pop

3501 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
3502 58885d79 Iustin Pop
      operations.
3503 58885d79 Iustin Pop

3504 58885d79 Iustin Pop
    @type blocking: boolean
3505 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
3506 58885d79 Iustin Pop
        can lock the file or return immediately
3507 58885d79 Iustin Pop
    @type timeout: int or None
3508 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
3509 58885d79 Iustin Pop
        (in blocking mode)
3510 a87b4824 Michael Hanselmann

3511 a87b4824 Michael Hanselmann
    """
3512 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
3513 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
3514 a87b4824 Michael Hanselmann
3515 a87b4824 Michael Hanselmann
3516 339be5a8 Michael Hanselmann
class LineSplitter:
3517 339be5a8 Michael Hanselmann
  """Splits data chunks into lines separated by newline.
3518 339be5a8 Michael Hanselmann

3519 339be5a8 Michael Hanselmann
  Instances provide a file-like interface.
3520 339be5a8 Michael Hanselmann

3521 339be5a8 Michael Hanselmann
  """
3522 339be5a8 Michael Hanselmann
  def __init__(self, line_fn, *args):
3523 339be5a8 Michael Hanselmann
    """Initializes this class.
3524 339be5a8 Michael Hanselmann

3525 339be5a8 Michael Hanselmann
    @type line_fn: callable
3526 339be5a8 Michael Hanselmann
    @param line_fn: Function called for each line, first parameter is line
3527 339be5a8 Michael Hanselmann
    @param args: Extra arguments for L{line_fn}
3528 339be5a8 Michael Hanselmann

3529 339be5a8 Michael Hanselmann
    """
3530 339be5a8 Michael Hanselmann
    assert callable(line_fn)
3531 339be5a8 Michael Hanselmann
3532 339be5a8 Michael Hanselmann
    if args:
3533 339be5a8 Michael Hanselmann
      # Python 2.4 doesn't have functools.partial yet
3534 339be5a8 Michael Hanselmann
      self._line_fn = \
3535 339be5a8 Michael Hanselmann
        lambda line: line_fn(line, *args) # pylint: disable-msg=W0142
3536 339be5a8 Michael Hanselmann
    else:
3537 339be5a8 Michael Hanselmann
      self._line_fn = line_fn
3538 339be5a8 Michael Hanselmann
3539 339be5a8 Michael Hanselmann
    self._lines = collections.deque()
3540 339be5a8 Michael Hanselmann
    self._buffer = ""
3541 339be5a8 Michael Hanselmann
3542 339be5a8 Michael Hanselmann
  def write(self, data):
3543 339be5a8 Michael Hanselmann
    parts = (self._buffer + data).split("\n")
3544 339be5a8 Michael Hanselmann
    self._buffer = parts.pop()
3545 339be5a8 Michael Hanselmann
    self._lines.extend(parts)
3546 339be5a8 Michael Hanselmann
3547 339be5a8 Michael Hanselmann
  def flush(self):
3548 339be5a8 Michael Hanselmann
    while self._lines:
3549 339be5a8 Michael Hanselmann
      self._line_fn(self._lines.popleft().rstrip("\r\n"))
3550 339be5a8 Michael Hanselmann
3551 339be5a8 Michael Hanselmann
  def close(self):
3552 339be5a8 Michael Hanselmann
    self.flush()
3553 339be5a8 Michael Hanselmann
    if self._buffer:
3554 339be5a8 Michael Hanselmann
      self._line_fn(self._buffer)
3555 339be5a8 Michael Hanselmann
3556 339be5a8 Michael Hanselmann
3557 451575de Guido Trotter
def SignalHandled(signums):
3558 451575de Guido Trotter
  """Signal Handled decoration.
3559 451575de Guido Trotter

3560 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
3561 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
3562 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
3563 451575de Guido Trotter
  objects as values.
3564 451575de Guido Trotter

3565 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
3566 451575de Guido Trotter
  with different handlers.
3567 451575de Guido Trotter

3568 451575de Guido Trotter
  @type signums: list
3569 451575de Guido Trotter
  @param signums: signals to intercept
3570 451575de Guido Trotter

3571 451575de Guido Trotter
  """
3572 451575de Guido Trotter
  def wrap(fn):
3573 451575de Guido Trotter
    def sig_function(*args, **kwargs):
3574 451575de Guido Trotter
      assert 'signal_handlers' not in kwargs or \
3575 451575de Guido Trotter
             kwargs['signal_handlers'] is None or \
3576 451575de Guido Trotter
             isinstance(kwargs['signal_handlers'], dict), \
3577 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
3578 451575de Guido Trotter
      if 'signal_handlers' in kwargs and kwargs['signal_handlers'] is not None:
3579 451575de Guido Trotter
        signal_handlers = kwargs['signal_handlers']
3580 451575de Guido Trotter
      else:
3581 451575de Guido Trotter
        signal_handlers = {}
3582 451575de Guido Trotter
        kwargs['signal_handlers'] = signal_handlers
3583 451575de Guido Trotter
      sighandler = SignalHandler(signums)
3584 451575de Guido Trotter
      try:
3585 451575de Guido Trotter
        for sig in signums:
3586 451575de Guido Trotter
          signal_handlers[sig] = sighandler
3587 451575de Guido Trotter
        return fn(*args, **kwargs)
3588 451575de Guido Trotter
      finally:
3589 451575de Guido Trotter
        sighandler.Reset()
3590 451575de Guido Trotter
    return sig_function
3591 451575de Guido Trotter
  return wrap
3592 451575de Guido Trotter
3593 451575de Guido Trotter
3594 b9768937 Michael Hanselmann
class SignalWakeupFd(object):
3595 b9768937 Michael Hanselmann
  try:
3596 b9768937 Michael Hanselmann
    # This is only supported in Python 2.5 and above (some distributions
3597 b9768937 Michael Hanselmann
    # backported it to Python 2.4)
3598 b9768937 Michael Hanselmann
    _set_wakeup_fd_fn = signal.set_wakeup_fd
3599 b9768937 Michael Hanselmann
  except AttributeError:
3600 b9768937 Michael Hanselmann
    # Not supported
3601 b9768937 Michael Hanselmann
    def _SetWakeupFd(self, _): # pylint: disable-msg=R0201
3602 b9768937 Michael Hanselmann
      return -1
3603 b9768937 Michael Hanselmann
  else:
3604 b9768937 Michael Hanselmann
    def _SetWakeupFd(self, fd):
3605 b9768937 Michael Hanselmann
      return self._set_wakeup_fd_fn(fd)
3606 b9768937 Michael Hanselmann
3607 b9768937 Michael Hanselmann
  def __init__(self):
3608 b9768937 Michael Hanselmann
    """Initializes this class.
3609 b9768937 Michael Hanselmann

3610 b9768937 Michael Hanselmann
    """
3611 b9768937 Michael Hanselmann
    (read_fd, write_fd) = os.pipe()
3612 b9768937 Michael Hanselmann
3613 b9768937 Michael Hanselmann
    # Once these succeeded, the file descriptors will be closed automatically.
3614 b9768937 Michael Hanselmann
    # Buffer size 0 is important, otherwise .read() with a specified length
3615 b9768937 Michael Hanselmann
    # might buffer data and the file descriptors won't be marked readable.
3616 b9768937 Michael Hanselmann
    self._read_fh = os.fdopen(read_fd, "r", 0)
3617 b9768937 Michael Hanselmann
    self._write_fh = os.fdopen(write_fd, "w", 0)
3618 b9768937 Michael Hanselmann
3619 b9768937 Michael Hanselmann
    self._previous = self._SetWakeupFd(self._write_fh.fileno())
3620 b9768937 Michael Hanselmann
3621 b9768937 Michael Hanselmann
    # Utility functions
3622 b9768937 Michael Hanselmann
    self.fileno = self._read_fh.fileno
3623 b9768937 Michael Hanselmann
    self.read = self._read_fh.read
3624 b9768937 Michael Hanselmann
3625 b9768937 Michael Hanselmann
  def Reset(self):
3626 b9768937 Michael Hanselmann
    """Restores the previous wakeup file descriptor.
3627 b9768937 Michael Hanselmann

3628 b9768937 Michael Hanselmann
    """
3629 b9768937 Michael Hanselmann
    if hasattr(self, "_previous") and self._previous is not None:
3630 b9768937 Michael Hanselmann
      self._SetWakeupFd(self._previous)
3631 b9768937 Michael Hanselmann
      self._previous = None
3632 b9768937 Michael Hanselmann
3633 b9768937 Michael Hanselmann
  def Notify(self):
3634 b9768937 Michael Hanselmann
    """Notifies the wakeup file descriptor.
3635 b9768937 Michael Hanselmann

3636 b9768937 Michael Hanselmann
    """
3637 b9768937 Michael Hanselmann
    self._write_fh.write("\0")
3638 b9768937 Michael Hanselmann
3639 b9768937 Michael Hanselmann
  def __del__(self):
3640 b9768937 Michael Hanselmann
    """Called before object deletion.
3641 b9768937 Michael Hanselmann

3642 b9768937 Michael Hanselmann
    """
3643 b9768937 Michael Hanselmann
    self.Reset()
3644 b9768937 Michael Hanselmann
3645 b9768937 Michael Hanselmann
3646 de499029 Michael Hanselmann
class SignalHandler(object):
3647 de499029 Michael Hanselmann
  """Generic signal handler class.
3648 de499029 Michael Hanselmann

3649 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
3650 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
3651 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
3652 58885d79 Iustin Pop
  signal was sent.
3653 58885d79 Iustin Pop

3654 58885d79 Iustin Pop
  @type signum: list
3655 58885d79 Iustin Pop
  @ivar signum: the signals we handle
3656 58885d79 Iustin Pop
  @type called: boolean
3657 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
3658 de499029 Michael Hanselmann

3659 de499029 Michael Hanselmann
  """
3660 b9768937 Michael Hanselmann
  def __init__(self, signum, handler_fn=None, wakeup=None):
3661 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
3662 de499029 Michael Hanselmann

3663 58885d79 Iustin Pop
    @type signum: int or list of ints
3664 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
3665 92b61ec7 Michael Hanselmann
    @type handler_fn: callable
3666 92b61ec7 Michael Hanselmann
    @param handler_fn: Signal handling function
3667 de499029 Michael Hanselmann

3668 de499029 Michael Hanselmann
    """
3669 92b61ec7 Michael Hanselmann
    assert handler_fn is None or callable(handler_fn)
3670 92b61ec7 Michael Hanselmann
3671 6c52849e Guido Trotter
    self.signum = set(signum)
3672 de499029 Michael Hanselmann
    self.called = False
3673 de499029 Michael Hanselmann
3674 92b61ec7 Michael Hanselmann
    self._handler_fn = handler_fn
3675 b9768937 Michael Hanselmann
    self._wakeup = wakeup
3676 92b61ec7 Michael Hanselmann
3677 de499029 Michael Hanselmann
    self._previous = {}
3678 de499029 Michael Hanselmann
    try:
3679 de499029 Michael Hanselmann
      for signum in self.signum:
3680 de499029 Michael Hanselmann
        # Setup handler
3681 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
3682 de499029 Michael Hanselmann
        try:
3683 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
3684 de499029 Michael Hanselmann
        except:
3685 de499029 Michael Hanselmann
          # Restore previous handler
3686 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
3687 de499029 Michael Hanselmann
          raise
3688 de499029 Michael Hanselmann
    except:
3689 de499029 Michael Hanselmann
      # Reset all handlers
3690 de499029 Michael Hanselmann
      self.Reset()
3691 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
3692 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
3693 de499029 Michael Hanselmann
      raise
3694 de499029 Michael Hanselmann
3695 de499029 Michael Hanselmann
  def __del__(self):
3696 de499029 Michael Hanselmann
    self.Reset()
3697 de499029 Michael Hanselmann
3698 de499029 Michael Hanselmann
  def Reset(self):
3699 de499029 Michael Hanselmann
    """Restore previous handler.
3700 de499029 Michael Hanselmann

3701 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
3702 58885d79 Iustin Pop

3703 de499029 Michael Hanselmann
    """
3704 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
3705 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
3706 de499029 Michael Hanselmann
      # If successful, remove from dict
3707 de499029 Michael Hanselmann
      del self._previous[signum]
3708 de499029 Michael Hanselmann
3709 de499029 Michael Hanselmann
  def Clear(self):
3710 58885d79 Iustin Pop
    """Unsets the L{called} flag.
3711 de499029 Michael Hanselmann

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

3714 de499029 Michael Hanselmann
    """
3715 de499029 Michael Hanselmann
    self.called = False
3716 de499029 Michael Hanselmann
3717 92b61ec7 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
3718 de499029 Michael Hanselmann
    """Actual signal handling function.
3719 de499029 Michael Hanselmann

3720 de499029 Michael Hanselmann
    """
3721 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
3722 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
3723 de499029 Michael Hanselmann
    self.called = True
3724 a2d2e1a7 Iustin Pop
3725 b9768937 Michael Hanselmann
    if self._wakeup:
3726 b9768937 Michael Hanselmann
      # Notify whoever is interested in signals
3727 b9768937 Michael Hanselmann
      self._wakeup.Notify()
3728 b9768937 Michael Hanselmann
3729 92b61ec7 Michael Hanselmann
    if self._handler_fn:
3730 92b61ec7 Michael Hanselmann
      self._handler_fn(signum, frame)
3731 92b61ec7 Michael Hanselmann
3732 a2d2e1a7 Iustin Pop
3733 a2d2e1a7 Iustin Pop
class FieldSet(object):
3734 a2d2e1a7 Iustin Pop
  """A simple field set.
3735 a2d2e1a7 Iustin Pop

3736 a2d2e1a7 Iustin Pop
  Among the features are:
3737 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
3738 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
3739 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
3740 a2d2e1a7 Iustin Pop

3741 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
3742 a2d2e1a7 Iustin Pop

3743 a2d2e1a7 Iustin Pop
  """
3744 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
3745 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
3746 a2d2e1a7 Iustin Pop
3747 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
3748 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
3749 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
3750 a2d2e1a7 Iustin Pop
3751 a2d2e1a7 Iustin Pop
  def Matches(self, field):
3752 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
3753 a2d2e1a7 Iustin Pop

3754 a2d2e1a7 Iustin Pop
    @type field: str
3755 a2d2e1a7 Iustin Pop
    @param field: the string to match
3756 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
3757 a2d2e1a7 Iustin Pop

3758 a2d2e1a7 Iustin Pop
    """
3759 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
3760 a2d2e1a7 Iustin Pop
      return m
3761 6c881c52 Iustin Pop
    return None
3762 a2d2e1a7 Iustin Pop
3763 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
3764 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
3765 a2d2e1a7 Iustin Pop

3766 a2d2e1a7 Iustin Pop
    @type items: list
3767 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
3768 a2d2e1a7 Iustin Pop
    @rtype: list
3769 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
3770 a2d2e1a7 Iustin Pop

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