Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 26d3fd2f

History | View | Annotate | Download (102.3 kB)

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

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

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

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

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

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

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

168 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
169 a8083063 Iustin Pop
  closed.
170 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

538 73027ed2 Michael Hanselmann
  @type fd: int
539 73027ed2 Michael Hanselmann
  @param fd: File descriptor
540 73027ed2 Michael Hanselmann
  @type enable: bool
541 73027ed2 Michael Hanselmann
  @param enable: Whether to set or unset it.
542 73027ed2 Michael Hanselmann

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

557 287a1740 Michael Hanselmann
  @type fd: int
558 287a1740 Michael Hanselmann
  @param fd: File descriptor
559 287a1740 Michael Hanselmann
  @type enable: bool
560 287a1740 Michael Hanselmann
  @param enable: Whether to set or unset it
561 287a1740 Michael Hanselmann

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

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

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

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

630 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
631 a8083063 Iustin Pop
  errors are passed.
632 a8083063 Iustin Pop

633 58885d79 Iustin Pop
  @type filename: str
634 58885d79 Iustin Pop
  @param filename: the file to be removed
635 58885d79 Iustin Pop

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

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

651 72087dcd Balazs Lecz
  @type dirname: str
652 72087dcd Balazs Lecz
  @param dirname: the empty directory to be removed
653 72087dcd Balazs Lecz

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

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

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

693 76e5f8b5 Michael Hanselmann
  This is a wrapper around C{os.makedirs} adding error handling not implemented
694 76e5f8b5 Michael Hanselmann
  before Python 2.5.
695 76e5f8b5 Michael Hanselmann

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

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

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

732 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
733 a8083063 Iustin Pop
  instead.
734 a8083063 Iustin Pop

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

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

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

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

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

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

850 a01b500b Michael Hanselmann
  @type pid: int
851 a01b500b Michael Hanselmann
  @param pid: Process ID
852 a01b500b Michael Hanselmann
  @rtype: string
853 a01b500b Michael Hanselmann

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

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

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

896 a01b500b Michael Hanselmann
  This is the opposite of the Linux kernel's fs/proc/array.c:render_sigset_t
897 a01b500b Michael Hanselmann
  function.
898 a01b500b Michael Hanselmann

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

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

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

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

951 a01b500b Michael Hanselmann
  @type pid: int
952 a01b500b Michael Hanselmann
  @param pid: Process ID
953 a01b500b Michael Hanselmann
  @type signum: int
954 a01b500b Michael Hanselmann
  @param signum: Signal number
955 a01b500b Michael Hanselmann
  @rtype: bool
956 a01b500b Michael Hanselmann

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

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

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

1006 debed9ae Michael Hanselmann
  This can be used together with L{StartDaemon}.
1007 debed9ae Michael Hanselmann

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

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

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

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

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

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

1083 28f34048 Michael Hanselmann
  @type name: number or string
1084 28f34048 Michael Hanselmann
  @param name: Service name or port specification
1085 28f34048 Michael Hanselmann

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

1107 58885d79 Iustin Pop
  @rtype: dict
1108 58885d79 Iustin Pop
  @return:
1109 58885d79 Iustin Pop
       Dictionary with keys volume name and values
1110 58885d79 Iustin Pop
       the size of the volume
1111 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

1210 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
1211 a8083063 Iustin Pop
  side.
1212 a8083063 Iustin Pop

1213 58885d79 Iustin Pop
  @type word: str
1214 58885d79 Iustin Pop
  @param word: the word to check
1215 58885d79 Iustin Pop
  @rtype: boolean
1216 58885d79 Iustin Pop
  @return: True if the word is 'safe'
1217 58885d79 Iustin Pop

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

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

1230 58885d79 Iustin Pop
  @type template: str
1231 58885d79 Iustin Pop
  @param template: the string holding the template for the
1232 58885d79 Iustin Pop
      string formatting
1233 58885d79 Iustin Pop
  @rtype: str
1234 58885d79 Iustin Pop
  @return: the expanded command line
1235 58885d79 Iustin Pop

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

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

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

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

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

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

1332 31155d60 Balazs Lecz
  @type cpu_mask: str
1333 31155d60 Balazs Lecz
  @param cpu_mask: CPU mask definition
1334 31155d60 Balazs Lecz
  @rtype: list of int
1335 31155d60 Balazs Lecz
  @return: list of CPU IDs
1336 31155d60 Balazs Lecz

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

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

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

1400 58885d79 Iustin Pop
  @type file_name: str
1401 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
1402 58885d79 Iustin Pop
  @type key: str
1403 58885d79 Iustin Pop
  @param key: string containing key
1404 58885d79 Iustin Pop

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

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

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

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

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

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

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

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

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

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

1533 1d466a4f Michael Hanselmann
  The format doesn't contain colons as some shells and applications them as
1534 1d466a4f Michael Hanselmann
  separators.
1535 1d466a4f Michael Hanselmann

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

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

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

1576 58885d79 Iustin Pop
  @type value: str
1577 58885d79 Iustin Pop
  @param value: the argument to be quoted
1578 58885d79 Iustin Pop
  @rtype: str
1579 58885d79 Iustin Pop
  @return: the quoted value
1580 58885d79 Iustin Pop

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

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

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

1603 858905fb Michael Hanselmann
  """
1604 858905fb Michael Hanselmann
  INDENT_STR = "  "
1605 858905fb Michael Hanselmann
1606 858905fb Michael Hanselmann
  def __init__(self, fh):
1607 858905fb Michael Hanselmann
    """Initializes this class.
1608 858905fb Michael Hanselmann

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

1616 858905fb Michael Hanselmann
    """
1617 858905fb Michael Hanselmann
    self._indent += 1
1618 858905fb Michael Hanselmann
1619 858905fb Michael Hanselmann
  def DecIndent(self):
1620 858905fb Michael Hanselmann
    """Decrease indentation level by 1.
1621 858905fb Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

1709 9dae41ad Guido Trotter
  @param dirs: list of tuples (dir_name, dir_mode)
1710 9dae41ad Guido Trotter
  @type dirs: list of (string, integer)
1711 9dae41ad Guido Trotter

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1893 dfdc4060 Guido Trotter
  Immediately returns at the first interruption.
1894 dfdc4060 Guido Trotter

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

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

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

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

1955 dfdc4060 Guido Trotter
  Retries until the timeout is expired, even if interrupted.
1956 dfdc4060 Guido Trotter

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

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

1984 f7414041 Michael Hanselmann
  Element order is preserved.
1985 58885d79 Iustin Pop

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

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

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

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

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

2009 1862d460 Alexander Schreiber
  """
2010 8fb00704 Iustin Pop
  if not _MAC_CHECK.match(mac):
2011 82187135 Renรฉ Nussbaumer
    raise errors.OpPrereqError("Invalid MAC address specified: %s" %
2012 82187135 Renรฉ Nussbaumer
                               mac, errors.ECODE_INVAL)
2013 82187135 Renรฉ Nussbaumer
2014 82187135 Renรฉ Nussbaumer
  return mac.lower()
2015 06009e27 Iustin Pop
2016 06009e27 Iustin Pop
2017 06009e27 Iustin Pop
def TestDelay(duration):
2018 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
2019 06009e27 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2222 b330ac0b Guido Trotter
  Any errors are ignored.
2223 b330ac0b Guido Trotter

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2405 de3b8e39 Luca Bigliardi
    Also open /dev/console to report errors while logging.
2406 de3b8e39 Luca Bigliardi

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

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

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

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

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

2524 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
2525 da961187 Guido Trotter

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

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

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

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

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

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

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

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

2596 27e46076 Michael Hanselmann
  @type value: string
2597 27e46076 Michael Hanselmann
  @param value: ASN1 GENERALIZEDTIME timestamp
2598 27e46076 Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

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

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

2796 615aaaba Michael Hanselmann
  HMAC is defined in RFC2104.
2797 615aaaba Michael Hanselmann

2798 615aaaba Michael Hanselmann
  @type key: string
2799 615aaaba Michael Hanselmann
  @param key: Secret key
2800 615aaaba Michael Hanselmann
  @type text: string
2801 615aaaba Michael Hanselmann

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

2814 615aaaba Michael Hanselmann
  HMAC is defined in RFC2104.
2815 615aaaba Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2951 1b045f5d Balazs Lecz
  This function is Linux-specific.
2952 1b045f5d Balazs Lecz

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

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

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

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

2987 eb58f7bd Michael Hanselmann
  Note: Only boolean return values are supported.
2988 eb58f7bd Michael Hanselmann

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

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

3034 560cbec1 Michael Hanselmann
  ESRCH is raised when a process is not found.
3035 560cbec1 Michael Hanselmann

3036 560cbec1 Michael Hanselmann
  @rtype: bool
3037 560cbec1 Michael Hanselmann
  @return: Whether process was found
3038 560cbec1 Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3521 339be5a8 Michael Hanselmann
  Instances provide a file-like interface.
3522 339be5a8 Michael Hanselmann

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

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

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

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

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

3570 451575de Guido Trotter
  @type signums: list
3571 451575de Guido Trotter
  @param signums: signals to intercept
3572 451575de Guido Trotter

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

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

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

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

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

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

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

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

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

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

3703 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
3704 58885d79 Iustin Pop

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

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

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

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

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

3743 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
3744 a2d2e1a7 Iustin Pop

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

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

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

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

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