Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 46d0a3d0

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

829 a01b500b Michael Hanselmann
  @type pid: int
830 a01b500b Michael Hanselmann
  @param pid: Process ID
831 a01b500b Michael Hanselmann
  @rtype: string
832 a01b500b Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

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

985 debed9ae Michael Hanselmann
  This can be used together with L{StartDaemon}.
986 debed9ae Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1307 31155d60 Balazs Lecz
  CPU mask format: comma-separated list of CPU IDs
1308 31155d60 Balazs Lecz
  or dash-separated ID ranges
1309 31155d60 Balazs Lecz
  Example: "0-2,5" -> "0,1,2,5"
1310 31155d60 Balazs Lecz

1311 31155d60 Balazs Lecz
  @type cpu_mask: str
1312 31155d60 Balazs Lecz
  @param cpu_mask: CPU mask definition
1313 31155d60 Balazs Lecz
  @rtype: list of int
1314 31155d60 Balazs Lecz
  @return: list of CPU IDs
1315 31155d60 Balazs Lecz

1316 31155d60 Balazs Lecz
  """
1317 31155d60 Balazs Lecz
  if not cpu_mask:
1318 31155d60 Balazs Lecz
    return []
1319 31155d60 Balazs Lecz
  cpu_list = []
1320 31155d60 Balazs Lecz
  for range_def in cpu_mask.split(","):
1321 31155d60 Balazs Lecz
    boundaries = range_def.split("-")
1322 31155d60 Balazs Lecz
    n_elements = len(boundaries)
1323 31155d60 Balazs Lecz
    if n_elements > 2:
1324 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID range definition"
1325 31155d60 Balazs Lecz
                              " (only one hyphen allowed): %s" % range_def)
1326 31155d60 Balazs Lecz
    try:
1327 31155d60 Balazs Lecz
      lower = int(boundaries[0])
1328 31155d60 Balazs Lecz
    except (ValueError, TypeError), err:
1329 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID value for lower boundary of"
1330 31155d60 Balazs Lecz
                              " CPU ID range: %s" % str(err))
1331 31155d60 Balazs Lecz
    try:
1332 31155d60 Balazs Lecz
      higher = int(boundaries[-1])
1333 31155d60 Balazs Lecz
    except (ValueError, TypeError), err:
1334 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID value for higher boundary of"
1335 31155d60 Balazs Lecz
                              " CPU ID range: %s" % str(err))
1336 31155d60 Balazs Lecz
    if lower > higher:
1337 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID range definition"
1338 31155d60 Balazs Lecz
                              " (%d > %d): %s" % (lower, higher, range_def))
1339 31155d60 Balazs Lecz
    cpu_list.extend(range(lower, higher + 1))
1340 31155d60 Balazs Lecz
  return cpu_list
1341 31155d60 Balazs Lecz
1342 31155d60 Balazs Lecz
1343 3727671e Renรฉ Nussbaumer
def AddAuthorizedKey(file_obj, key):
1344 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
1345 a8083063 Iustin Pop

1346 3727671e Renรฉ Nussbaumer
  @type file_obj: str or file handle
1347 3727671e Renรฉ Nussbaumer
  @param file_obj: path to authorized_keys file
1348 58885d79 Iustin Pop
  @type key: str
1349 58885d79 Iustin Pop
  @param key: string containing key
1350 58885d79 Iustin Pop

1351 a8083063 Iustin Pop
  """
1352 a8083063 Iustin Pop
  key_fields = key.split()
1353 a8083063 Iustin Pop
1354 3727671e Renรฉ Nussbaumer
  if isinstance(file_obj, basestring):
1355 3727671e Renรฉ Nussbaumer
    f = open(file_obj, 'a+')
1356 3727671e Renรฉ Nussbaumer
  else:
1357 3727671e Renรฉ Nussbaumer
    f = file_obj
1358 3727671e Renรฉ Nussbaumer
1359 a8083063 Iustin Pop
  try:
1360 a8083063 Iustin Pop
    nl = True
1361 a8083063 Iustin Pop
    for line in f:
1362 a8083063 Iustin Pop
      # Ignore whitespace changes
1363 a8083063 Iustin Pop
      if line.split() == key_fields:
1364 a8083063 Iustin Pop
        break
1365 a8083063 Iustin Pop
      nl = line.endswith('\n')
1366 a8083063 Iustin Pop
    else:
1367 a8083063 Iustin Pop
      if not nl:
1368 a8083063 Iustin Pop
        f.write("\n")
1369 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
1370 a8083063 Iustin Pop
      f.write("\n")
1371 a8083063 Iustin Pop
      f.flush()
1372 a8083063 Iustin Pop
  finally:
1373 a8083063 Iustin Pop
    f.close()
1374 a8083063 Iustin Pop
1375 a8083063 Iustin Pop
1376 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
1377 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
1378 a8083063 Iustin Pop

1379 58885d79 Iustin Pop
  @type file_name: str
1380 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
1381 58885d79 Iustin Pop
  @type key: str
1382 58885d79 Iustin Pop
  @param key: string containing key
1383 58885d79 Iustin Pop

1384 a8083063 Iustin Pop
  """
1385 a8083063 Iustin Pop
  key_fields = key.split()
1386 a8083063 Iustin Pop
1387 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1388 a8083063 Iustin Pop
  try:
1389 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
1390 a8083063 Iustin Pop
    try:
1391 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
1392 59f82e3f Michael Hanselmann
      try:
1393 59f82e3f Michael Hanselmann
        for line in f:
1394 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
1395 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
1396 59f82e3f Michael Hanselmann
            out.write(line)
1397 899d2a81 Michael Hanselmann
1398 899d2a81 Michael Hanselmann
        out.flush()
1399 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
1400 899d2a81 Michael Hanselmann
      finally:
1401 899d2a81 Michael Hanselmann
        f.close()
1402 899d2a81 Michael Hanselmann
    finally:
1403 899d2a81 Michael Hanselmann
      out.close()
1404 899d2a81 Michael Hanselmann
  except:
1405 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
1406 899d2a81 Michael Hanselmann
    raise
1407 899d2a81 Michael Hanselmann
1408 899d2a81 Michael Hanselmann
1409 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
1410 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
1411 899d2a81 Michael Hanselmann

1412 58885d79 Iustin Pop
  @type file_name: str
1413 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1414 58885d79 Iustin Pop
  @type ip: str
1415 58885d79 Iustin Pop
  @param ip: the IP address
1416 58885d79 Iustin Pop
  @type hostname: str
1417 58885d79 Iustin Pop
  @param hostname: the hostname to be added
1418 58885d79 Iustin Pop
  @type aliases: list
1419 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
1420 58885d79 Iustin Pop

1421 899d2a81 Michael Hanselmann
  """
1422 9b977740 Guido Trotter
  # FIXME: use WriteFile + fn rather than duplicating its efforts
1423 7fbb1f65 Michael Hanselmann
  # Ensure aliases are unique
1424 7fbb1f65 Michael Hanselmann
  aliases = UniqueSequence([hostname] + aliases)[1:]
1425 7fbb1f65 Michael Hanselmann
1426 9440aeab Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1427 899d2a81 Michael Hanselmann
  try:
1428 9440aeab Michael Hanselmann
    out = os.fdopen(fd, 'w')
1429 9440aeab Michael Hanselmann
    try:
1430 9440aeab Michael Hanselmann
      f = open(file_name, 'r')
1431 9440aeab Michael Hanselmann
      try:
1432 9440aeab Michael Hanselmann
        for line in f:
1433 9440aeab Michael Hanselmann
          fields = line.split()
1434 7e3dbb94 Iustin Pop
          if fields and not fields[0].startswith('#') and ip == fields[0]:
1435 9440aeab Michael Hanselmann
            continue
1436 9440aeab Michael Hanselmann
          out.write(line)
1437 9440aeab Michael Hanselmann
1438 7e3dbb94 Iustin Pop
        out.write("%s\t%s" % (ip, hostname))
1439 9440aeab Michael Hanselmann
        if aliases:
1440 9440aeab Michael Hanselmann
          out.write(" %s" % ' '.join(aliases))
1441 9440aeab Michael Hanselmann
        out.write('\n')
1442 9440aeab Michael Hanselmann
1443 9440aeab Michael Hanselmann
        out.flush()
1444 2e3e75b7 Michael Hanselmann
        os.fsync(out)
1445 9b977740 Guido Trotter
        os.chmod(tmpname, 0644)
1446 9440aeab Michael Hanselmann
        os.rename(tmpname, file_name)
1447 9440aeab Michael Hanselmann
      finally:
1448 9440aeab Michael Hanselmann
        f.close()
1449 9440aeab Michael Hanselmann
    finally:
1450 9440aeab Michael Hanselmann
      out.close()
1451 9440aeab Michael Hanselmann
  except:
1452 9440aeab Michael Hanselmann
    RemoveFile(tmpname)
1453 9440aeab Michael Hanselmann
    raise
1454 899d2a81 Michael Hanselmann
1455 899d2a81 Michael Hanselmann
1456 d9c02ca6 Michael Hanselmann
def AddHostToEtcHosts(hostname):
1457 d9c02ca6 Michael Hanselmann
  """Wrapper around SetEtcHostsEntry.
1458 d9c02ca6 Michael Hanselmann

1459 58885d79 Iustin Pop
  @type hostname: str
1460 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
1461 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1462 58885d79 Iustin Pop

1463 d9c02ca6 Michael Hanselmann
  """
1464 a744b676 Manuel Franceschini
  hi = netutils.HostInfo(name=hostname)
1465 d9c02ca6 Michael Hanselmann
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
1466 d9c02ca6 Michael Hanselmann
1467 d9c02ca6 Michael Hanselmann
1468 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
1469 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
1470 899d2a81 Michael Hanselmann

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

1473 58885d79 Iustin Pop
  @type file_name: str
1474 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1475 58885d79 Iustin Pop
  @type hostname: str
1476 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
1477 58885d79 Iustin Pop

1478 899d2a81 Michael Hanselmann
  """
1479 9b977740 Guido Trotter
  # FIXME: use WriteFile + fn rather than duplicating its efforts
1480 899d2a81 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1481 899d2a81 Michael Hanselmann
  try:
1482 899d2a81 Michael Hanselmann
    out = os.fdopen(fd, 'w')
1483 899d2a81 Michael Hanselmann
    try:
1484 899d2a81 Michael Hanselmann
      f = open(file_name, 'r')
1485 899d2a81 Michael Hanselmann
      try:
1486 899d2a81 Michael Hanselmann
        for line in f:
1487 899d2a81 Michael Hanselmann
          fields = line.split()
1488 899d2a81 Michael Hanselmann
          if len(fields) > 1 and not fields[0].startswith('#'):
1489 899d2a81 Michael Hanselmann
            names = fields[1:]
1490 899d2a81 Michael Hanselmann
            if hostname in names:
1491 899d2a81 Michael Hanselmann
              while hostname in names:
1492 899d2a81 Michael Hanselmann
                names.remove(hostname)
1493 899d2a81 Michael Hanselmann
              if names:
1494 9440aeab Michael Hanselmann
                out.write("%s %s\n" % (fields[0], ' '.join(names)))
1495 899d2a81 Michael Hanselmann
              continue
1496 899d2a81 Michael Hanselmann
1497 899d2a81 Michael Hanselmann
          out.write(line)
1498 59f82e3f Michael Hanselmann
1499 59f82e3f Michael Hanselmann
        out.flush()
1500 2e3e75b7 Michael Hanselmann
        os.fsync(out)
1501 9b977740 Guido Trotter
        os.chmod(tmpname, 0644)
1502 59f82e3f Michael Hanselmann
        os.rename(tmpname, file_name)
1503 59f82e3f Michael Hanselmann
      finally:
1504 59f82e3f Michael Hanselmann
        f.close()
1505 a8083063 Iustin Pop
    finally:
1506 59f82e3f Michael Hanselmann
      out.close()
1507 59f82e3f Michael Hanselmann
  except:
1508 59f82e3f Michael Hanselmann
    RemoveFile(tmpname)
1509 59f82e3f Michael Hanselmann
    raise
1510 a8083063 Iustin Pop
1511 a8083063 Iustin Pop
1512 d9c02ca6 Michael Hanselmann
def RemoveHostFromEtcHosts(hostname):
1513 d9c02ca6 Michael Hanselmann
  """Wrapper around RemoveEtcHostsEntry.
1514 d9c02ca6 Michael Hanselmann

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

1520 d9c02ca6 Michael Hanselmann
  """
1521 a744b676 Manuel Franceschini
  hi = netutils.HostInfo(name=hostname)
1522 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
1523 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
1524 d9c02ca6 Michael Hanselmann
1525 d9c02ca6 Michael Hanselmann
1526 1d466a4f Michael Hanselmann
def TimestampForFilename():
1527 1d466a4f Michael Hanselmann
  """Returns the current time formatted for filenames.
1528 1d466a4f Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2121 7d88772a Iustin Pop
  @type logfile: str
2122 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
2123 743b53d4 Renรฉ Nussbaumer
  @type run_uid: int
2124 743b53d4 Renรฉ Nussbaumer
  @param run_uid: Run the child under this uid
2125 743b53d4 Renรฉ Nussbaumer
  @type run_gid: int
2126 743b53d4 Renรฉ Nussbaumer
  @param run_gid: Run the child under this gid
2127 7d88772a Iustin Pop
  @rtype: int
2128 5fcc718f Iustin Pop
  @return: the value zero
2129 7d88772a Iustin Pop

2130 7d88772a Iustin Pop
  """
2131 7260cfbe Iustin Pop
  # pylint: disable-msg=W0212
2132 7260cfbe Iustin Pop
  # yes, we really want os._exit
2133 7d88772a Iustin Pop
  UMASK = 077
2134 7d88772a Iustin Pop
  WORKDIR = "/"
2135 8f765069 Iustin Pop
2136 8f765069 Iustin Pop
  # this might fail
2137 8f765069 Iustin Pop
  pid = os.fork()
2138 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
2139 8f765069 Iustin Pop
    os.setsid()
2140 743b53d4 Renรฉ Nussbaumer
    # FIXME: When removing again and moving to start-stop-daemon privilege drop
2141 743b53d4 Renรฉ Nussbaumer
    #        make sure to check for config permission and bail out when invoked
2142 743b53d4 Renรฉ Nussbaumer
    #        with wrong user.
2143 743b53d4 Renรฉ Nussbaumer
    os.setgid(run_gid)
2144 743b53d4 Renรฉ Nussbaumer
    os.setuid(run_uid)
2145 8f765069 Iustin Pop
    # this might fail
2146 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
2147 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
2148 8f765069 Iustin Pop
      os.chdir(WORKDIR)
2149 8f765069 Iustin Pop
      os.umask(UMASK)
2150 8f765069 Iustin Pop
    else:
2151 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
2152 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
2153 8f765069 Iustin Pop
  else:
2154 8f765069 Iustin Pop
    os._exit(0) # Exit parent of the first child.
2155 8f765069 Iustin Pop
2156 7d88772a Iustin Pop
  for fd in range(3):
2157 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
2158 7d88772a Iustin Pop
  i = os.open("/dev/null", os.O_RDONLY) # stdin
2159 7d88772a Iustin Pop
  assert i == 0, "Can't close/reopen stdin"
2160 7d88772a Iustin Pop
  i = os.open(logfile, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0600) # stdout
2161 7d88772a Iustin Pop
  assert i == 1, "Can't close/reopen stdout"
2162 7d88772a Iustin Pop
  # Duplicate standard output to standard error.
2163 7d88772a Iustin Pop
  os.dup2(1, 2)
2164 8f765069 Iustin Pop
  return 0
2165 57c177af Iustin Pop
2166 57c177af Iustin Pop
2167 53beffbb Iustin Pop
def DaemonPidFileName(name):
2168 58885d79 Iustin Pop
  """Compute a ganeti pid file absolute path
2169 58885d79 Iustin Pop

2170 58885d79 Iustin Pop
  @type name: str
2171 58885d79 Iustin Pop
  @param name: the daemon name
2172 58885d79 Iustin Pop
  @rtype: str
2173 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
2174 58885d79 Iustin Pop
      daemon name
2175 b330ac0b Guido Trotter

2176 b330ac0b Guido Trotter
  """
2177 c4feafe8 Iustin Pop
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
2178 b330ac0b Guido Trotter
2179 b330ac0b Guido Trotter
2180 2826b361 Guido Trotter
def EnsureDaemon(name):
2181 2826b361 Guido Trotter
  """Check for and start daemon if not alive.
2182 2826b361 Guido Trotter

2183 2826b361 Guido Trotter
  """
2184 2826b361 Guido Trotter
  result = RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
2185 2826b361 Guido Trotter
  if result.failed:
2186 2826b361 Guido Trotter
    logging.error("Can't start daemon '%s', failure %s, output: %s",
2187 2826b361 Guido Trotter
                  name, result.fail_reason, result.output)
2188 2826b361 Guido Trotter
    return False
2189 2826b361 Guido Trotter
2190 2826b361 Guido Trotter
  return True
2191 b330ac0b Guido Trotter
2192 b330ac0b Guido Trotter
2193 db147305 Tom Limoncelli
def StopDaemon(name):
2194 db147305 Tom Limoncelli
  """Stop daemon
2195 db147305 Tom Limoncelli

2196 db147305 Tom Limoncelli
  """
2197 db147305 Tom Limoncelli
  result = RunCmd([constants.DAEMON_UTIL, "stop", name])
2198 db147305 Tom Limoncelli
  if result.failed:
2199 db147305 Tom Limoncelli
    logging.error("Can't stop daemon '%s', failure %s, output: %s",
2200 db147305 Tom Limoncelli
                  name, result.fail_reason, result.output)
2201 db147305 Tom Limoncelli
    return False
2202 db147305 Tom Limoncelli
2203 db147305 Tom Limoncelli
  return True
2204 db147305 Tom Limoncelli
2205 db147305 Tom Limoncelli
2206 b330ac0b Guido Trotter
def WritePidFile(name):
2207 b330ac0b Guido Trotter
  """Write the current process pidfile.
2208 b330ac0b Guido Trotter

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

2211 58885d79 Iustin Pop
  @type name: str
2212 58885d79 Iustin Pop
  @param name: the daemon name to use
2213 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
2214 58885d79 Iustin Pop
      points to a live process
2215 b330ac0b Guido Trotter

2216 b330ac0b Guido Trotter
  """
2217 b330ac0b Guido Trotter
  pid = os.getpid()
2218 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
2219 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
2220 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
2221 b330ac0b Guido Trotter
2222 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
2223 b330ac0b Guido Trotter
2224 b330ac0b Guido Trotter
2225 b330ac0b Guido Trotter
def RemovePidFile(name):
2226 b330ac0b Guido Trotter
  """Remove the current process pidfile.
2227 b330ac0b Guido Trotter

2228 b330ac0b Guido Trotter
  Any errors are ignored.
2229 b330ac0b Guido Trotter

2230 58885d79 Iustin Pop
  @type name: str
2231 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
2232 58885d79 Iustin Pop

2233 b330ac0b Guido Trotter
  """
2234 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
2235 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
2236 b330ac0b Guido Trotter
  try:
2237 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
2238 7260cfbe Iustin Pop
  except: # pylint: disable-msg=W0702
2239 b330ac0b Guido Trotter
    pass
2240 b330ac0b Guido Trotter
2241 b330ac0b Guido Trotter
2242 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
2243 ff5251bc Iustin Pop
                waitpid=False):
2244 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
2245 b2a1f511 Iustin Pop

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

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

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

2311 58885d79 Iustin Pop
  @type name: str
2312 58885d79 Iustin Pop
  @param name: the name to look for
2313 58885d79 Iustin Pop
  @type search_path: str
2314 58885d79 Iustin Pop
  @param search_path: location to start at
2315 58885d79 Iustin Pop
  @type test: callable
2316 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
2317 58885d79 Iustin Pop
      if the a given object is valid; the default value is
2318 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
2319 58885d79 Iustin Pop
  @rtype: str or None
2320 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
2321 57c177af Iustin Pop

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

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

2345 58885d79 Iustin Pop
  @type vglist: dict
2346 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
2347 58885d79 Iustin Pop
  @type vgname: str
2348 58885d79 Iustin Pop
  @param vgname: the volume group we should check
2349 58885d79 Iustin Pop
  @type minsize: int
2350 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
2351 58885d79 Iustin Pop
  @rtype: None or str
2352 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
2353 8d1a2a64 Michael Hanselmann

2354 8d1a2a64 Michael Hanselmann
  """
2355 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
2356 8d1a2a64 Michael Hanselmann
  if vgsize is None:
2357 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
2358 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
2359 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
2360 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
2361 8d1a2a64 Michael Hanselmann
  return None
2362 7996a135 Iustin Pop
2363 7996a135 Iustin Pop
2364 45bc5e4a Michael Hanselmann
def SplitTime(value):
2365 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
2366 739be818 Michael Hanselmann

2367 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
2368 45bc5e4a Michael Hanselmann
  @type value: int or float
2369 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
2370 739be818 Michael Hanselmann

2371 739be818 Michael Hanselmann
  """
2372 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
2373 45bc5e4a Michael Hanselmann
2374 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
2375 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
2376 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
2377 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
2378 45bc5e4a Michael Hanselmann
2379 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
2380 739be818 Michael Hanselmann
2381 739be818 Michael Hanselmann
2382 739be818 Michael Hanselmann
def MergeTime(timetuple):
2383 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
2384 739be818 Michael Hanselmann

2385 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
2386 739be818 Michael Hanselmann
  @type timetuple: tuple
2387 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
2388 739be818 Michael Hanselmann

2389 739be818 Michael Hanselmann
  """
2390 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
2391 739be818 Michael Hanselmann
2392 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
2393 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
2394 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
2395 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
2396 739be818 Michael Hanselmann
2397 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
2398 739be818 Michael Hanselmann
2399 739be818 Michael Hanselmann
2400 de3b8e39 Luca Bigliardi
class LogFileHandler(logging.FileHandler):
2401 de3b8e39 Luca Bigliardi
  """Log handler that doesn't fallback to stderr.
2402 de3b8e39 Luca Bigliardi

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

2407 de3b8e39 Luca Bigliardi
  """
2408 de3b8e39 Luca Bigliardi
  def __init__(self, filename, mode="a", encoding=None):
2409 de3b8e39 Luca Bigliardi
    """Open the specified file and use it as the stream for logging.
2410 de3b8e39 Luca Bigliardi

2411 de3b8e39 Luca Bigliardi
    Also open /dev/console to report errors while logging.
2412 de3b8e39 Luca Bigliardi

2413 de3b8e39 Luca Bigliardi
    """
2414 de3b8e39 Luca Bigliardi
    logging.FileHandler.__init__(self, filename, mode, encoding)
2415 de3b8e39 Luca Bigliardi
    self.console = open(constants.DEV_CONSOLE, "a")
2416 de3b8e39 Luca Bigliardi
2417 20601361 Luca Bigliardi
  def handleError(self, record): # pylint: disable-msg=C0103
2418 de3b8e39 Luca Bigliardi
    """Handle errors which occur during an emit() call.
2419 de3b8e39 Luca Bigliardi

2420 de3b8e39 Luca Bigliardi
    Try to handle errors with FileHandler method, if it fails write to
2421 de3b8e39 Luca Bigliardi
    /dev/console.
2422 de3b8e39 Luca Bigliardi

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

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

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

2530 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
2531 da961187 Guido Trotter

2532 da961187 Guido Trotter
  """
2533 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
2534 82d9caef Iustin Pop
2535 016d04b3 Michael Hanselmann
2536 4bb678e9 Iustin Pop
def PathJoin(*args):
2537 4bb678e9 Iustin Pop
  """Safe-join a list of path components.
2538 4bb678e9 Iustin Pop

2539 4bb678e9 Iustin Pop
  Requirements:
2540 4bb678e9 Iustin Pop
      - the first argument must be an absolute path
2541 4bb678e9 Iustin Pop
      - no component in the path must have backtracking (e.g. /../),
2542 4bb678e9 Iustin Pop
        since we check for normalization at the end
2543 4bb678e9 Iustin Pop

2544 4bb678e9 Iustin Pop
  @param args: the path components to be joined
2545 4bb678e9 Iustin Pop
  @raise ValueError: for invalid paths
2546 4bb678e9 Iustin Pop

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

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

2573 f65f63ef Iustin Pop
  @param fname: the file name
2574 f65f63ef Iustin Pop
  @type lines: int
2575 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
2576 f65f63ef Iustin Pop

2577 f65f63ef Iustin Pop
  """
2578 f65f63ef Iustin Pop
  fd = open(fname, "r")
2579 f65f63ef Iustin Pop
  try:
2580 f65f63ef Iustin Pop
    fd.seek(0, 2)
2581 f65f63ef Iustin Pop
    pos = fd.tell()
2582 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
2583 f65f63ef Iustin Pop
    fd.seek(pos, 0)
2584 f65f63ef Iustin Pop
    raw_data = fd.read()
2585 f65f63ef Iustin Pop
  finally:
2586 f65f63ef Iustin Pop
    fd.close()
2587 f65f63ef Iustin Pop
2588 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
2589 f65f63ef Iustin Pop
  return rows[-lines:]
2590 f65f63ef Iustin Pop
2591 f65f63ef Iustin Pop
2592 24d70417 Michael Hanselmann
def FormatTimestampWithTZ(secs):
2593 24d70417 Michael Hanselmann
  """Formats a Unix timestamp with the local timezone.
2594 24d70417 Michael Hanselmann

2595 24d70417 Michael Hanselmann
  """
2596 24d70417 Michael Hanselmann
  return time.strftime("%F %T %Z", time.gmtime(secs))
2597 24d70417 Michael Hanselmann
2598 24d70417 Michael Hanselmann
2599 27e46076 Michael Hanselmann
def _ParseAsn1Generalizedtime(value):
2600 27e46076 Michael Hanselmann
  """Parses an ASN1 GENERALIZEDTIME timestamp as used by pyOpenSSL.
2601 27e46076 Michael Hanselmann

2602 27e46076 Michael Hanselmann
  @type value: string
2603 27e46076 Michael Hanselmann
  @param value: ASN1 GENERALIZEDTIME timestamp
2604 27e46076 Michael Hanselmann

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

2629 27e46076 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2630 27e46076 Michael Hanselmann
  @param cert: X509 certificate object
2631 27e46076 Michael Hanselmann

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

2666 24d70417 Michael Hanselmann
  @type expired: bool
2667 24d70417 Michael Hanselmann
  @param expired: Whether pyOpenSSL considers the certificate as expired
2668 24d70417 Michael Hanselmann
  @type not_before: number or None
2669 24d70417 Michael Hanselmann
  @param not_before: Unix timestamp before which certificate is not valid
2670 24d70417 Michael Hanselmann
  @type not_after: number or None
2671 24d70417 Michael Hanselmann
  @param not_after: Unix timestamp after which certificate is invalid
2672 24d70417 Michael Hanselmann
  @type now: number
2673 24d70417 Michael Hanselmann
  @param now: Current time as Unix timestamp
2674 24d70417 Michael Hanselmann
  @type warn_days: number or None
2675 24d70417 Michael Hanselmann
  @param warn_days: How many days before expiration a warning should be reported
2676 24d70417 Michael Hanselmann
  @type error_days: number or None
2677 24d70417 Michael Hanselmann
  @param error_days: How many days before expiration an error should be reported
2678 24d70417 Michael Hanselmann

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

2716 24d70417 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2717 24d70417 Michael Hanselmann
  @param cert: X509 certificate object
2718 24d70417 Michael Hanselmann
  @type warn_days: number or None
2719 24d70417 Michael Hanselmann
  @param warn_days: How many days before expiration a warning should be reported
2720 24d70417 Michael Hanselmann
  @type error_days: number or None
2721 24d70417 Michael Hanselmann
  @param error_days: How many days before expiration an error should be reported
2722 24d70417 Michael Hanselmann

2723 24d70417 Michael Hanselmann
  """
2724 24d70417 Michael Hanselmann
  # Depending on the pyOpenSSL version, this can just return (None, None)
2725 24d70417 Michael Hanselmann
  (not_before, not_after) = GetX509CertValidity(cert)
2726 24d70417 Michael Hanselmann
2727 24d70417 Michael Hanselmann
  return _VerifyCertificateInner(cert.has_expired(), not_before, not_after,
2728 24d70417 Michael Hanselmann
                                 time.time(), warn_days, error_days)
2729 24d70417 Michael Hanselmann
2730 24d70417 Michael Hanselmann
2731 68857643 Michael Hanselmann
def SignX509Certificate(cert, key, salt):
2732 68857643 Michael Hanselmann
  """Sign a X509 certificate.
2733 68857643 Michael Hanselmann

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

2736 68857643 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2737 68857643 Michael Hanselmann
  @param cert: X509 certificate object
2738 68857643 Michael Hanselmann
  @type key: string
2739 68857643 Michael Hanselmann
  @param key: Key for HMAC
2740 68857643 Michael Hanselmann
  @type salt: string
2741 68857643 Michael Hanselmann
  @param salt: Salt for HMAC
2742 68857643 Michael Hanselmann
  @rtype: string
2743 68857643 Michael Hanselmann
  @return: Serialized and signed certificate in PEM format
2744 68857643 Michael Hanselmann

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

2761 68857643 Michael Hanselmann
  """
2762 68857643 Michael Hanselmann
  # Extract signature from original PEM data
2763 68857643 Michael Hanselmann
  for line in cert_pem.splitlines():
2764 68857643 Michael Hanselmann
    if line.startswith("---"):
2765 68857643 Michael Hanselmann
      break
2766 68857643 Michael Hanselmann
2767 68857643 Michael Hanselmann
    m = X509_SIGNATURE.match(line.strip())
2768 68857643 Michael Hanselmann
    if m:
2769 68857643 Michael Hanselmann
      return (m.group("salt"), m.group("sign"))
2770 68857643 Michael Hanselmann
2771 68857643 Michael Hanselmann
  raise errors.GenericError("X509 certificate signature is missing")
2772 68857643 Michael Hanselmann
2773 68857643 Michael Hanselmann
2774 68857643 Michael Hanselmann
def LoadSignedX509Certificate(cert_pem, key):
2775 68857643 Michael Hanselmann
  """Verifies a signed X509 certificate.
2776 68857643 Michael Hanselmann

2777 68857643 Michael Hanselmann
  @type cert_pem: string
2778 68857643 Michael Hanselmann
  @param cert_pem: Certificate in PEM format and with signature header
2779 68857643 Michael Hanselmann
  @type key: string
2780 68857643 Michael Hanselmann
  @param key: Key for HMAC
2781 68857643 Michael Hanselmann
  @rtype: tuple; (OpenSSL.crypto.X509, string)
2782 68857643 Michael Hanselmann
  @return: X509 certificate object and salt
2783 68857643 Michael Hanselmann

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

2802 615aaaba Michael Hanselmann
  HMAC is defined in RFC2104.
2803 615aaaba Michael Hanselmann

2804 615aaaba Michael Hanselmann
  @type key: string
2805 615aaaba Michael Hanselmann
  @param key: Secret key
2806 615aaaba Michael Hanselmann
  @type text: string
2807 615aaaba Michael Hanselmann

2808 615aaaba Michael Hanselmann
  """
2809 3718bf6d Michael Hanselmann
  if salt:
2810 3718bf6d Michael Hanselmann
    salted_text = salt + text
2811 3718bf6d Michael Hanselmann
  else:
2812 3718bf6d Michael Hanselmann
    salted_text = text
2813 3718bf6d Michael Hanselmann
2814 716a32cb Guido Trotter
  return hmac.new(key, salted_text, compat.sha1).hexdigest()
2815 615aaaba Michael Hanselmann
2816 615aaaba Michael Hanselmann
2817 3718bf6d Michael Hanselmann
def VerifySha1Hmac(key, text, digest, salt=None):
2818 615aaaba Michael Hanselmann
  """Verifies the HMAC-SHA1 digest of a text.
2819 615aaaba Michael Hanselmann

2820 615aaaba Michael Hanselmann
  HMAC is defined in RFC2104.
2821 615aaaba Michael Hanselmann

2822 615aaaba Michael Hanselmann
  @type key: string
2823 615aaaba Michael Hanselmann
  @param key: Secret key
2824 615aaaba Michael Hanselmann
  @type text: string
2825 615aaaba Michael Hanselmann
  @type digest: string
2826 615aaaba Michael Hanselmann
  @param digest: Expected digest
2827 615aaaba Michael Hanselmann
  @rtype: bool
2828 615aaaba Michael Hanselmann
  @return: Whether HMAC-SHA1 digest matches
2829 615aaaba Michael Hanselmann

2830 615aaaba Michael Hanselmann
  """
2831 3718bf6d Michael Hanselmann
  return digest.lower() == Sha1Hmac(key, text, salt=salt).lower()
2832 615aaaba Michael Hanselmann
2833 615aaaba Michael Hanselmann
2834 26f15862 Iustin Pop
def SafeEncode(text):
2835 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
2836 26f15862 Iustin Pop

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

2846 26f15862 Iustin Pop
  @type text: str or unicode
2847 26f15862 Iustin Pop
  @param text: input data
2848 26f15862 Iustin Pop
  @rtype: str
2849 26f15862 Iustin Pop
  @return: a safe version of text
2850 26f15862 Iustin Pop

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

2874 5b69bc7c Iustin Pop
  This function splits a string based on a separator where the
2875 5b69bc7c Iustin Pop
  separator itself can be escape in order to be an element of the
2876 5b69bc7c Iustin Pop
  elements. The escaping rules are (assuming coma being the
2877 5b69bc7c Iustin Pop
  separator):
2878 5b69bc7c Iustin Pop
    - a plain , separates the elements
2879 5b69bc7c Iustin Pop
    - a sequence \\\\, (double backslash plus comma) is handled as a
2880 5b69bc7c Iustin Pop
      backslash plus a separator comma
2881 5b69bc7c Iustin Pop
    - a sequence \, (backslash plus comma) is handled as a
2882 5b69bc7c Iustin Pop
      non-separator comma
2883 5b69bc7c Iustin Pop

2884 5b69bc7c Iustin Pop
  @type text: string
2885 5b69bc7c Iustin Pop
  @param text: the string to split
2886 5b69bc7c Iustin Pop
  @type sep: string
2887 5b69bc7c Iustin Pop
  @param text: the separator
2888 5b69bc7c Iustin Pop
  @rtype: string
2889 5b69bc7c Iustin Pop
  @return: a list of strings
2890 5b69bc7c Iustin Pop

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

2916 ab3e6da8 Iustin Pop
  @param names: set, list or tuple
2917 ab3e6da8 Iustin Pop
  @return: a string with the formatted results
2918 ab3e6da8 Iustin Pop

2919 ab3e6da8 Iustin Pop
  """
2920 1f864b60 Iustin Pop
  return ", ".join([str(val) for val in names])
2921 ab3e6da8 Iustin Pop
2922 ab3e6da8 Iustin Pop
2923 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
2924 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
2925 3f6a47a8 Michael Hanselmann

2926 3f6a47a8 Michael Hanselmann
  @type value: int
2927 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
2928 3f6a47a8 Michael Hanselmann
  @rtype: int
2929 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
2930 3f6a47a8 Michael Hanselmann

2931 3f6a47a8 Michael Hanselmann
  """
2932 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
2933 3f6a47a8 Michael Hanselmann
2934 3f6a47a8 Michael Hanselmann
2935 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
2936 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
2937 3f6a47a8 Michael Hanselmann

2938 3f6a47a8 Michael Hanselmann
  @type path: string
2939 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
2940 3f6a47a8 Michael Hanselmann
  @rtype: int
2941 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
2942 3f6a47a8 Michael Hanselmann

2943 3f6a47a8 Michael Hanselmann
  """
2944 3f6a47a8 Michael Hanselmann
  size = 0
2945 3f6a47a8 Michael Hanselmann
2946 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
2947 2a887df9 Michael Hanselmann
    for filename in files:
2948 c4feafe8 Iustin Pop
      st = os.lstat(PathJoin(curpath, filename))
2949 3f6a47a8 Michael Hanselmann
      size += st.st_size
2950 3f6a47a8 Michael Hanselmann
2951 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
2952 3f6a47a8 Michael Hanselmann
2953 3f6a47a8 Michael Hanselmann
2954 1b045f5d Balazs Lecz
def GetMounts(filename=constants.PROC_MOUNTS):
2955 1b045f5d Balazs Lecz
  """Returns the list of mounted filesystems.
2956 1b045f5d Balazs Lecz

2957 1b045f5d Balazs Lecz
  This function is Linux-specific.
2958 1b045f5d Balazs Lecz

2959 1b045f5d Balazs Lecz
  @param filename: path of mounts file (/proc/mounts by default)
2960 1b045f5d Balazs Lecz
  @rtype: list of tuples
2961 1b045f5d Balazs Lecz
  @return: list of mount entries (device, mountpoint, fstype, options)
2962 1b045f5d Balazs Lecz

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

2977 3f6a47a8 Michael Hanselmann
  @type path: string
2978 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
2979 3f6a47a8 Michael Hanselmann
  @rtype: int
2980 620a85fd Iustin Pop
  @return: tuple of (Total space, Free space) in mebibytes
2981 3f6a47a8 Michael Hanselmann

2982 3f6a47a8 Michael Hanselmann
  """
2983 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
2984 3f6a47a8 Michael Hanselmann
2985 620a85fd Iustin Pop
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
2986 620a85fd Iustin Pop
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
2987 620a85fd Iustin Pop
  return (tsize, fsize)
2988 3f6a47a8 Michael Hanselmann
2989 3f6a47a8 Michael Hanselmann
2990 bdefe5dd Michael Hanselmann
def RunInSeparateProcess(fn, *args):
2991 eb58f7bd Michael Hanselmann
  """Runs a function in a separate process.
2992 eb58f7bd Michael Hanselmann

2993 eb58f7bd Michael Hanselmann
  Note: Only boolean return values are supported.
2994 eb58f7bd Michael Hanselmann

2995 eb58f7bd Michael Hanselmann
  @type fn: callable
2996 eb58f7bd Michael Hanselmann
  @param fn: Function to be called
2997 bdefe5dd Michael Hanselmann
  @rtype: bool
2998 bdefe5dd Michael Hanselmann
  @return: Function's result
2999 eb58f7bd Michael Hanselmann

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

3040 560cbec1 Michael Hanselmann
  ESRCH is raised when a process is not found.
3041 560cbec1 Michael Hanselmann

3042 560cbec1 Michael Hanselmann
  @rtype: bool
3043 560cbec1 Michael Hanselmann
  @return: Whether process was found
3044 560cbec1 Michael Hanselmann

3045 560cbec1 Michael Hanselmann
  """
3046 560cbec1 Michael Hanselmann
  try:
3047 560cbec1 Michael Hanselmann
    fn(*args, **kwargs)
3048 560cbec1 Michael Hanselmann
  except EnvironmentError, err:
3049 560cbec1 Michael Hanselmann
    # Ignore ESRCH
3050 560cbec1 Michael Hanselmann
    if err.errno == errno.ESRCH:
3051 560cbec1 Michael Hanselmann
      return False
3052 560cbec1 Michael Hanselmann
    raise
3053 560cbec1 Michael Hanselmann
3054 560cbec1 Michael Hanselmann
  return True
3055 560cbec1 Michael Hanselmann
3056 560cbec1 Michael Hanselmann
3057 232144d0 Guido Trotter
def IgnoreSignals(fn, *args, **kwargs):
3058 232144d0 Guido Trotter
  """Tries to call a function ignoring failures due to EINTR.
3059 232144d0 Guido Trotter

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

3080 58885d79 Iustin Pop
  @type fd: int
3081 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
3082 58885d79 Iustin Pop

3083 eb0f0ce0 Michael Hanselmann
  """
3084 eb0f0ce0 Michael Hanselmann
  try:
3085 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
3086 eb0f0ce0 Michael Hanselmann
  except IOError, err:
3087 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
3088 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
3089 eb0f0ce0 Michael Hanselmann
    raise
3090 de499029 Michael Hanselmann
3091 de499029 Michael Hanselmann
3092 3b813dd2 Iustin Pop
def FormatTime(val):
3093 3b813dd2 Iustin Pop
  """Formats a time value.
3094 3b813dd2 Iustin Pop

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

3099 3b813dd2 Iustin Pop
  """
3100 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
3101 3b813dd2 Iustin Pop
    return "N/A"
3102 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
3103 3b813dd2 Iustin Pop
  # platforms
3104 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
3105 3b813dd2 Iustin Pop
3106 3b813dd2 Iustin Pop
3107 f8ea4ada Michael Hanselmann
def FormatSeconds(secs):
3108 f8ea4ada Michael Hanselmann
  """Formats seconds for easier reading.
3109 f8ea4ada Michael Hanselmann

3110 f8ea4ada Michael Hanselmann
  @type secs: number
3111 f8ea4ada Michael Hanselmann
  @param secs: Number of seconds
3112 f8ea4ada Michael Hanselmann
  @rtype: string
3113 f8ea4ada Michael Hanselmann
  @return: Formatted seconds (e.g. "2d 9h 19m 49s")
3114 f8ea4ada Michael Hanselmann

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

3135 5cbe43a5 Michael Hanselmann
  @type filename: string
3136 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
3137 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
3138 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
3139 5cbe43a5 Michael Hanselmann
  @type remove_after: int
3140 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
3141 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
3142 5cbe43a5 Michael Hanselmann

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

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

3182 de0ea66b Michael Hanselmann
  """
3183 506be7c5 Guido Trotter
  def RaiseInner(self):
3184 506be7c5 Guido Trotter
    if self.args and isinstance(self.args[0], Exception):
3185 506be7c5 Guido Trotter
      raise self.args[0]
3186 506be7c5 Guido Trotter
    else:
3187 506be7c5 Guido Trotter
      raise RetryTimeout(*self.args)
3188 de0ea66b Michael Hanselmann
3189 de0ea66b Michael Hanselmann
3190 de0ea66b Michael Hanselmann
class RetryAgain(Exception):
3191 de0ea66b Michael Hanselmann
  """Retry again.
3192 de0ea66b Michael Hanselmann

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

3197 de0ea66b Michael Hanselmann
  """
3198 de0ea66b Michael Hanselmann
3199 de0ea66b Michael Hanselmann
3200 de0ea66b Michael Hanselmann
class _RetryDelayCalculator(object):
3201 de0ea66b Michael Hanselmann
  """Calculator for increasing delays.
3202 de0ea66b Michael Hanselmann

3203 de0ea66b Michael Hanselmann
  """
3204 de0ea66b Michael Hanselmann
  __slots__ = [
3205 de0ea66b Michael Hanselmann
    "_factor",
3206 de0ea66b Michael Hanselmann
    "_limit",
3207 de0ea66b Michael Hanselmann
    "_next",
3208 de0ea66b Michael Hanselmann
    "_start",
3209 de0ea66b Michael Hanselmann
    ]
3210 de0ea66b Michael Hanselmann
3211 de0ea66b Michael Hanselmann
  def __init__(self, start, factor, limit):
3212 de0ea66b Michael Hanselmann
    """Initializes this class.
3213 de0ea66b Michael Hanselmann

3214 de0ea66b Michael Hanselmann
    @type start: float
3215 de0ea66b Michael Hanselmann
    @param start: Initial delay
3216 de0ea66b Michael Hanselmann
    @type factor: float
3217 de0ea66b Michael Hanselmann
    @param factor: Factor for delay increase
3218 de0ea66b Michael Hanselmann
    @type limit: float or None
3219 de0ea66b Michael Hanselmann
    @param limit: Upper limit for delay or None for no limit
3220 de0ea66b Michael Hanselmann

3221 de0ea66b Michael Hanselmann
    """
3222 de0ea66b Michael Hanselmann
    assert start > 0.0
3223 de0ea66b Michael Hanselmann
    assert factor >= 1.0
3224 de0ea66b Michael Hanselmann
    assert limit is None or limit >= 0.0
3225 de0ea66b Michael Hanselmann
3226 de0ea66b Michael Hanselmann
    self._start = start
3227 de0ea66b Michael Hanselmann
    self._factor = factor
3228 de0ea66b Michael Hanselmann
    self._limit = limit
3229 de0ea66b Michael Hanselmann
3230 de0ea66b Michael Hanselmann
    self._next = start
3231 de0ea66b Michael Hanselmann
3232 de0ea66b Michael Hanselmann
  def __call__(self):
3233 de0ea66b Michael Hanselmann
    """Returns current delay and calculates the next one.
3234 de0ea66b Michael Hanselmann

3235 de0ea66b Michael Hanselmann
    """
3236 de0ea66b Michael Hanselmann
    current = self._next
3237 de0ea66b Michael Hanselmann
3238 de0ea66b Michael Hanselmann
    # Update for next run
3239 de0ea66b Michael Hanselmann
    if self._limit is None or self._next < self._limit:
3240 26751075 Michael Hanselmann
      self._next = min(self._limit, self._next * self._factor)
3241 de0ea66b Michael Hanselmann
3242 de0ea66b Michael Hanselmann
    return current
3243 de0ea66b Michael Hanselmann
3244 de0ea66b Michael Hanselmann
3245 de0ea66b Michael Hanselmann
#: Special delay to specify whole remaining timeout
3246 de0ea66b Michael Hanselmann
RETRY_REMAINING_TIME = object()
3247 de0ea66b Michael Hanselmann
3248 de0ea66b Michael Hanselmann
3249 de0ea66b Michael Hanselmann
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep,
3250 de0ea66b Michael Hanselmann
          _time_fn=time.time):
3251 de0ea66b Michael Hanselmann
  """Call a function repeatedly until it succeeds.
3252 de0ea66b Michael Hanselmann

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

3257 de0ea66b Michael Hanselmann
  C{delay} can be one of the following:
3258 de0ea66b Michael Hanselmann
    - callable returning the delay length as a float
3259 de0ea66b Michael Hanselmann
    - Tuple of (start, factor, limit)
3260 de0ea66b Michael Hanselmann
    - L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is
3261 de0ea66b Michael Hanselmann
      useful when overriding L{wait_fn} to wait for an external event)
3262 de0ea66b Michael Hanselmann
    - A static delay as a number (int or float)
3263 de0ea66b Michael Hanselmann

3264 de0ea66b Michael Hanselmann
  @type fn: callable
3265 de0ea66b Michael Hanselmann
  @param fn: Function to be called
3266 de0ea66b Michael Hanselmann
  @param delay: Either a callable (returning the delay), a tuple of (start,
3267 de0ea66b Michael Hanselmann
                factor, limit) (see L{_RetryDelayCalculator}),
3268 de0ea66b Michael Hanselmann
                L{RETRY_REMAINING_TIME} or a number (int or float)
3269 de0ea66b Michael Hanselmann
  @type timeout: float
3270 de0ea66b Michael Hanselmann
  @param timeout: Total timeout
3271 de0ea66b Michael Hanselmann
  @type wait_fn: callable
3272 de0ea66b Michael Hanselmann
  @param wait_fn: Waiting function
3273 de0ea66b Michael Hanselmann
  @return: Return value of function
3274 de0ea66b Michael Hanselmann

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

3334 bdd5e420 Michael Hanselmann
  """
3335 bdd5e420 Michael Hanselmann
  (fd, path) = tempfile.mkstemp(*args, **kwargs)
3336 bdd5e420 Michael Hanselmann
  _CloseFDNoErr(fd)
3337 bdd5e420 Michael Hanselmann
  return path
3338 bdd5e420 Michael Hanselmann
3339 bdd5e420 Michael Hanselmann
3340 bdd5e420 Michael Hanselmann
def GenerateSelfSignedX509Cert(common_name, validity):
3341 bdd5e420 Michael Hanselmann
  """Generates a self-signed X509 certificate.
3342 bdd5e420 Michael Hanselmann

3343 bdd5e420 Michael Hanselmann
  @type common_name: string
3344 bdd5e420 Michael Hanselmann
  @param common_name: commonName value
3345 a55474c7 Michael Hanselmann
  @type validity: int
3346 bdd5e420 Michael Hanselmann
  @param validity: Validity for certificate in seconds
3347 a55474c7 Michael Hanselmann

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

3374 2ea65c7d Manuel Franceschini
  @type filename: str
3375 2ea65c7d Manuel Franceschini
  @param filename: path to write certificate to
3376 600535f0 Manuel Franceschini
  @type common_name: string
3377 600535f0 Manuel Franceschini
  @param common_name: commonName value
3378 600535f0 Manuel Franceschini
  @type validity: int
3379 600535f0 Manuel Franceschini
  @param validity: validity of certificate in number of days
3380 600535f0 Manuel Franceschini

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

3394 a87b4824 Michael Hanselmann
  """
3395 b4478d34 Michael Hanselmann
  def __init__(self, fd, filename):
3396 58885d79 Iustin Pop
    """Constructor for FileLock.
3397 58885d79 Iustin Pop

3398 b4478d34 Michael Hanselmann
    @type fd: file
3399 b4478d34 Michael Hanselmann
    @param fd: File object
3400 58885d79 Iustin Pop
    @type filename: str
3401 b4478d34 Michael Hanselmann
    @param filename: Path of the file opened at I{fd}
3402 58885d79 Iustin Pop

3403 58885d79 Iustin Pop
    """
3404 b4478d34 Michael Hanselmann
    self.fd = fd
3405 a87b4824 Michael Hanselmann
    self.filename = filename
3406 b4478d34 Michael Hanselmann
3407 b4478d34 Michael Hanselmann
  @classmethod
3408 b4478d34 Michael Hanselmann
  def Open(cls, filename):
3409 b4478d34 Michael Hanselmann
    """Creates and opens a file to be used as a file-based lock.
3410 b4478d34 Michael Hanselmann

3411 b4478d34 Michael Hanselmann
    @type filename: string
3412 b4478d34 Michael Hanselmann
    @param filename: path to the file to be locked
3413 b4478d34 Michael Hanselmann

3414 b4478d34 Michael Hanselmann
    """
3415 b4478d34 Michael Hanselmann
    # Using "os.open" is necessary to allow both opening existing file
3416 b4478d34 Michael Hanselmann
    # read/write and creating if not existing. Vanilla "open" will truncate an
3417 b4478d34 Michael Hanselmann
    # existing file -or- allow creating if not existing.
3418 b4478d34 Michael Hanselmann
    return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), "w+"),
3419 b4478d34 Michael Hanselmann
               filename)
3420 a87b4824 Michael Hanselmann
3421 a87b4824 Michael Hanselmann
  def __del__(self):
3422 a87b4824 Michael Hanselmann
    self.Close()
3423 a87b4824 Michael Hanselmann
3424 a87b4824 Michael Hanselmann
  def Close(self):
3425 58885d79 Iustin Pop
    """Close the file and release the lock.
3426 58885d79 Iustin Pop

3427 58885d79 Iustin Pop
    """
3428 aac3fbf0 Iustin Pop
    if hasattr(self, "fd") and self.fd:
3429 a87b4824 Michael Hanselmann
      self.fd.close()
3430 a87b4824 Michael Hanselmann
      self.fd = None
3431 a87b4824 Michael Hanselmann
3432 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
3433 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
3434 aa74b828 Michael Hanselmann

3435 aa74b828 Michael Hanselmann
    @type flag: int
3436 58885d79 Iustin Pop
    @param flag: operation flag
3437 aa74b828 Michael Hanselmann
    @type blocking: bool
3438 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
3439 aa74b828 Michael Hanselmann
    @type timeout: None or float
3440 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
3441 aa74b828 Michael Hanselmann
                    non-blocking mode).
3442 aa74b828 Michael Hanselmann
    @type errmsg: string
3443 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
3444 aa74b828 Michael Hanselmann

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

3478 58885d79 Iustin Pop
    @type blocking: boolean
3479 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
3480 58885d79 Iustin Pop
        can lock the file or return immediately
3481 58885d79 Iustin Pop
    @type timeout: int or None
3482 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
3483 58885d79 Iustin Pop
        (in blocking mode)
3484 58885d79 Iustin Pop

3485 a87b4824 Michael Hanselmann
    """
3486 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
3487 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
3488 a87b4824 Michael Hanselmann
3489 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
3490 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
3491 a87b4824 Michael Hanselmann

3492 58885d79 Iustin Pop
    @type blocking: boolean
3493 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
3494 58885d79 Iustin Pop
        can lock the file or return immediately
3495 58885d79 Iustin Pop
    @type timeout: int or None
3496 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
3497 58885d79 Iustin Pop
        (in blocking mode)
3498 58885d79 Iustin Pop

3499 a87b4824 Michael Hanselmann
    """
3500 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
3501 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
3502 a87b4824 Michael Hanselmann
3503 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
3504 a87b4824 Michael Hanselmann
    """Unlocks the file.
3505 a87b4824 Michael Hanselmann

3506 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
3507 58885d79 Iustin Pop
    operation::
3508 58885d79 Iustin Pop

3509 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
3510 58885d79 Iustin Pop
      operations.
3511 58885d79 Iustin Pop

3512 58885d79 Iustin Pop
    @type blocking: boolean
3513 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
3514 58885d79 Iustin Pop
        can lock the file or return immediately
3515 58885d79 Iustin Pop
    @type timeout: int or None
3516 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
3517 58885d79 Iustin Pop
        (in blocking mode)
3518 a87b4824 Michael Hanselmann

3519 a87b4824 Michael Hanselmann
    """
3520 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
3521 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
3522 a87b4824 Michael Hanselmann
3523 a87b4824 Michael Hanselmann
3524 339be5a8 Michael Hanselmann
class LineSplitter:
3525 339be5a8 Michael Hanselmann
  """Splits data chunks into lines separated by newline.
3526 339be5a8 Michael Hanselmann

3527 339be5a8 Michael Hanselmann
  Instances provide a file-like interface.
3528 339be5a8 Michael Hanselmann

3529 339be5a8 Michael Hanselmann
  """
3530 339be5a8 Michael Hanselmann
  def __init__(self, line_fn, *args):
3531 339be5a8 Michael Hanselmann
    """Initializes this class.
3532 339be5a8 Michael Hanselmann

3533 339be5a8 Michael Hanselmann
    @type line_fn: callable
3534 339be5a8 Michael Hanselmann
    @param line_fn: Function called for each line, first parameter is line
3535 339be5a8 Michael Hanselmann
    @param args: Extra arguments for L{line_fn}
3536 339be5a8 Michael Hanselmann

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

3568 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
3569 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
3570 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
3571 451575de Guido Trotter
  objects as values.
3572 451575de Guido Trotter

3573 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
3574 451575de Guido Trotter
  with different handlers.
3575 451575de Guido Trotter

3576 451575de Guido Trotter
  @type signums: list
3577 451575de Guido Trotter
  @param signums: signals to intercept
3578 451575de Guido Trotter

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

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

3636 b9768937 Michael Hanselmann
    """
3637 b9768937 Michael Hanselmann
    if hasattr(self, "_previous") and self._previous is not None:
3638 b9768937 Michael Hanselmann
      self._SetWakeupFd(self._previous)
3639 b9768937 Michael Hanselmann
      self._previous = None
3640 b9768937 Michael Hanselmann
3641 b9768937 Michael Hanselmann
  def Notify(self):
3642 b9768937 Michael Hanselmann
    """Notifies the wakeup file descriptor.
3643 b9768937 Michael Hanselmann

3644 b9768937 Michael Hanselmann
    """
3645 b9768937 Michael Hanselmann
    self._write_fh.write("\0")
3646 b9768937 Michael Hanselmann
3647 b9768937 Michael Hanselmann
  def __del__(self):
3648 b9768937 Michael Hanselmann
    """Called before object deletion.
3649 b9768937 Michael Hanselmann

3650 b9768937 Michael Hanselmann
    """
3651 b9768937 Michael Hanselmann
    self.Reset()
3652 b9768937 Michael Hanselmann
3653 b9768937 Michael Hanselmann
3654 de499029 Michael Hanselmann
class SignalHandler(object):
3655 de499029 Michael Hanselmann
  """Generic signal handler class.
3656 de499029 Michael Hanselmann

3657 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
3658 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
3659 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
3660 58885d79 Iustin Pop
  signal was sent.
3661 58885d79 Iustin Pop

3662 58885d79 Iustin Pop
  @type signum: list
3663 58885d79 Iustin Pop
  @ivar signum: the signals we handle
3664 58885d79 Iustin Pop
  @type called: boolean
3665 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
3666 de499029 Michael Hanselmann

3667 de499029 Michael Hanselmann
  """
3668 b9768937 Michael Hanselmann
  def __init__(self, signum, handler_fn=None, wakeup=None):
3669 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
3670 de499029 Michael Hanselmann

3671 58885d79 Iustin Pop
    @type signum: int or list of ints
3672 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
3673 92b61ec7 Michael Hanselmann
    @type handler_fn: callable
3674 92b61ec7 Michael Hanselmann
    @param handler_fn: Signal handling function
3675 de499029 Michael Hanselmann

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

3709 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
3710 58885d79 Iustin Pop

3711 de499029 Michael Hanselmann
    """
3712 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
3713 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
3714 de499029 Michael Hanselmann
      # If successful, remove from dict
3715 de499029 Michael Hanselmann
      del self._previous[signum]
3716 de499029 Michael Hanselmann
3717 de499029 Michael Hanselmann
  def Clear(self):
3718 58885d79 Iustin Pop
    """Unsets the L{called} flag.
3719 de499029 Michael Hanselmann

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

3722 de499029 Michael Hanselmann
    """
3723 de499029 Michael Hanselmann
    self.called = False
3724 de499029 Michael Hanselmann
3725 92b61ec7 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
3726 de499029 Michael Hanselmann
    """Actual signal handling function.
3727 de499029 Michael Hanselmann

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

3744 a2d2e1a7 Iustin Pop
  Among the features are:
3745 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
3746 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
3747 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
3748 a2d2e1a7 Iustin Pop

3749 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
3750 a2d2e1a7 Iustin Pop

3751 a2d2e1a7 Iustin Pop
  """
3752 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
3753 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
3754 a2d2e1a7 Iustin Pop
3755 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
3756 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
3757 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
3758 a2d2e1a7 Iustin Pop
3759 a2d2e1a7 Iustin Pop
  def Matches(self, field):
3760 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
3761 a2d2e1a7 Iustin Pop

3762 a2d2e1a7 Iustin Pop
    @type field: str
3763 a2d2e1a7 Iustin Pop
    @param field: the string to match
3764 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
3765 a2d2e1a7 Iustin Pop

3766 a2d2e1a7 Iustin Pop
    """
3767 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
3768 a2d2e1a7 Iustin Pop
      return m
3769 6c881c52 Iustin Pop
    return None
3770 a2d2e1a7 Iustin Pop
3771 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
3772 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
3773 a2d2e1a7 Iustin Pop

3774 a2d2e1a7 Iustin Pop
    @type items: list
3775 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
3776 a2d2e1a7 Iustin Pop
    @rtype: list
3777 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
3778 a2d2e1a7 Iustin Pop

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